feat: introduce support for Talos API access from Kubernetes

This is a first step: providing a service to access Talos API.

Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
This commit is contained in:
Andrey Smirnov 2022-05-05 15:56:00 +03:00
parent 34d3a41643
commit 3addea83b9
No known key found for this signature in database
GPG Key ID: 7B26396447AB6DFD
28 changed files with 1078 additions and 6 deletions

View File

@ -280,6 +280,8 @@ func (ctrl *K8sControlPlaneController) manageManifestsConfig(ctx context.Context
FlannelCNIImage: images.FlannelCNI,
PodSecurityPolicyEnabled: !cfgProvider.Cluster().APIServer().DisablePodSecurityPolicy(),
TalosAPIServiceEnabled: cfgProvider.Machine().Features().KubernetesTalosAPIAccess().Enabled(),
}
return nil

View File

@ -19,6 +19,7 @@ import (
"go.uber.org/zap"
k8sadapter "github.com/talos-systems/talos/internal/app/machined/pkg/adapters/k8s"
"github.com/talos-systems/talos/pkg/machinery/constants"
"github.com/talos-systems/talos/pkg/machinery/resources/k8s"
"github.com/talos-systems/talos/pkg/machinery/resources/secrets"
)
@ -159,9 +160,19 @@ func (ctrl *ManifestController) render(cfg k8s.BootstrapManifestsConfigSpec, scr
k8s.BootstrapManifestsConfigSpec
Secrets *secrets.KubernetesRootSpec
KubernetesTalosAPIServiceName string
KubernetesTalosAPIServiceNamespace string
ApidPort int
}{
BootstrapManifestsConfigSpec: cfg,
Secrets: scrt,
KubernetesTalosAPIServiceName: constants.KubernetesTalosAPIServiceName,
KubernetesTalosAPIServiceNamespace: constants.KubernetesTalosAPIServiceNamespace,
ApidPort: constants.ApidPort,
}
type manifestDesc struct {
@ -211,6 +222,14 @@ func (ctrl *ManifestController) render(cfg k8s.BootstrapManifestsConfigSpec, scr
)
}
if cfg.TalosAPIServiceEnabled {
defaultManifests = append(defaultManifests,
[]manifestDesc{
{"12-talos-api-service", talosAPIService},
}...,
)
}
manifests := make([]renderedManifest, len(defaultManifests))
for i := range defaultManifests {

View File

@ -700,3 +700,21 @@ spec:
- min: 1
max: 65536
`)
// talosAPIService is the service to access Talos API from Kubernetes.
// Service exposes the Endpoints which are managed by controllers.
var talosAPIService = []byte(`apiVersion: v1
kind: Service
metadata:
labels:
component: apid
provider: talos
name: {{ .KubernetesTalosAPIServiceName }}
namespace: {{ .KubernetesTalosAPIServiceNamespace }}
spec:
ports:
- name: apid
port: {{ .ApidPort }}
protocol: TCP
targetPort: {{ .ApidPort }}
`)

View File

@ -0,0 +1,113 @@
// 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 kubeaccess
import (
"context"
"fmt"
"github.com/cosi-project/runtime/pkg/controller"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/state"
"github.com/siderolabs/go-pointer"
"go.uber.org/zap"
"github.com/talos-systems/talos/pkg/machinery/resources/config"
"github.com/talos-systems/talos/pkg/machinery/resources/kubeaccess"
)
// ConfigController watches v1alpha1.Config, updates Talos API access config.
type ConfigController struct{}
// Name implements controller.Controller interface.
func (ctrl *ConfigController) Name() string {
return "kubeaccess.ConfigController"
}
// Inputs implements controller.Controller interface.
func (ctrl *ConfigController) Inputs() []controller.Input {
return []controller.Input{
{
Namespace: config.NamespaceName,
Type: config.MachineTypeType,
ID: pointer.To(config.MachineTypeID),
Kind: controller.InputWeak,
},
{
Namespace: config.NamespaceName,
Type: config.MachineConfigType,
ID: pointer.To(config.V1Alpha1ID),
Kind: controller.InputWeak,
},
}
}
// Outputs implements controller.Controller interface.
func (ctrl *ConfigController) Outputs() []controller.Output {
return []controller.Output{
{
Type: kubeaccess.ConfigType,
Kind: controller.OutputExclusive,
},
}
}
// Run implements controller.Controller interface.
//
//nolint:gocyclo
func (ctrl *ConfigController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error {
for {
select {
case <-ctx.Done():
return nil
case <-r.EventCh():
}
machineType, err := r.Get(ctx, resource.NewMetadata(config.NamespaceName, config.MachineTypeType, config.MachineTypeID, resource.VersionUndefined))
if err != nil {
if state.IsNotFoundError(err) {
continue
}
return fmt.Errorf("error getting machine type: %w", err)
}
if !machineType.(*config.MachineType).MachineType().IsControlPlane() {
if err = r.Destroy(ctx, kubeaccess.NewConfig(config.NamespaceName, kubeaccess.ConfigID).Metadata()); err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error destroying kubeaccess config: %w", err)
}
}
// not a control plane node, nothing to do
continue
}
cfg, err := r.Get(ctx, resource.NewMetadata(config.NamespaceName, config.MachineConfigType, config.V1Alpha1ID, resource.VersionUndefined))
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error getting config: %w", err)
}
}
if err = r.Modify(ctx, kubeaccess.NewConfig(config.NamespaceName, kubeaccess.ConfigID), func(res resource.Resource) error {
spec := res.(*kubeaccess.Config).TypedSpec()
*spec = kubeaccess.ConfigSpec{}
if cfg != nil {
c := cfg.(*config.MachineConfig).Config()
spec.Enabled = c.Machine().Features().KubernetesTalosAPIAccess().Enabled()
spec.AllowedAPIRoles = c.Machine().Features().KubernetesTalosAPIAccess().AllowedRoles()
spec.AllowedKubernetesNamespaces = c.Machine().Features().KubernetesTalosAPIAccess().AllowedKubernetesNamespaces()
}
return nil
}); err != nil {
return err
}
}
}

View File

@ -0,0 +1,139 @@
// 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 kubeaccess_test
import (
"testing"
"time"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/siderolabs/go-pointer"
"github.com/stretchr/testify/suite"
"github.com/talos-systems/go-retry/retry"
kubeaccessctrl "github.com/talos-systems/talos/internal/app/machined/pkg/controllers/kubeaccess"
"github.com/talos-systems/talos/pkg/machinery/config/types/v1alpha1"
"github.com/talos-systems/talos/pkg/machinery/config/types/v1alpha1/machine"
"github.com/talos-systems/talos/pkg/machinery/resources/config"
"github.com/talos-systems/talos/pkg/machinery/resources/kubeaccess"
)
type ConfigSuite struct {
KubeaccessSuite
}
func (suite *ConfigSuite) TestReconcileConfig() {
suite.Require().NoError(suite.runtime.RegisterController(&kubeaccessctrl.ConfigController{}))
suite.startRuntime()
machineType := config.NewMachineType()
machineType.SetMachineType(machine.TypeControlPlane)
suite.Require().NoError(suite.state.Create(suite.ctx, machineType))
cfg := config.NewMachineConfig(&v1alpha1.Config{
ConfigVersion: "v1alpha1",
MachineConfig: &v1alpha1.MachineConfig{
MachineFeatures: &v1alpha1.FeaturesConfig{
KubernetesTalosAPIAccessConfig: &v1alpha1.KubernetesTalosAPIAccessConfig{
AccessEnabled: pointer.To(true),
AccessAllowedRoles: []string{"os:admin"},
AccessAllowedKubernetesNamespaces: []string{"kube-system"},
},
},
},
})
suite.Require().NoError(suite.state.Create(suite.ctx, cfg))
specMD := resource.NewMetadata(config.NamespaceName, kubeaccess.ConfigType, kubeaccess.ConfigID, resource.VersionUndefined)
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
suite.assertResource(
specMD,
func(res resource.Resource) error {
spec := res.(*kubeaccess.Config).TypedSpec()
suite.Assert().True(spec.Enabled)
suite.Assert().Equal([]string{"os:admin"}, spec.AllowedAPIRoles)
suite.Assert().Equal([]string{"kube-system"}, spec.AllowedKubernetesNamespaces)
return nil
},
),
))
}
func (suite *ConfigSuite) TestReconcileDisabled() {
suite.Require().NoError(suite.runtime.RegisterController(&kubeaccessctrl.ConfigController{}))
suite.startRuntime()
machineType := config.NewMachineType()
machineType.SetMachineType(machine.TypeInit)
suite.Require().NoError(suite.state.Create(suite.ctx, machineType))
cfg := config.NewMachineConfig(&v1alpha1.Config{
ConfigVersion: "v1alpha1",
MachineConfig: &v1alpha1.MachineConfig{},
})
suite.Require().NoError(suite.state.Create(suite.ctx, cfg))
specMD := resource.NewMetadata(config.NamespaceName, kubeaccess.ConfigType, kubeaccess.ConfigID, resource.VersionUndefined)
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
suite.assertResource(
specMD,
func(res resource.Resource) error {
spec := res.(*kubeaccess.Config).TypedSpec()
suite.Assert().False(spec.Enabled)
suite.Assert().Empty(spec.AllowedAPIRoles)
suite.Assert().Empty(spec.AllowedKubernetesNamespaces)
return nil
},
),
))
}
func (suite *ConfigSuite) TestReconcileWorker() {
suite.Require().NoError(suite.runtime.RegisterController(&kubeaccessctrl.ConfigController{}))
suite.startRuntime()
machineType := config.NewMachineType()
machineType.SetMachineType(machine.TypeWorker)
suite.Require().NoError(suite.state.Create(suite.ctx, machineType))
cfg := config.NewMachineConfig(&v1alpha1.Config{
ConfigVersion: "v1alpha1",
MachineConfig: &v1alpha1.MachineConfig{
MachineFeatures: &v1alpha1.FeaturesConfig{
KubernetesTalosAPIAccessConfig: &v1alpha1.KubernetesTalosAPIAccessConfig{
AccessEnabled: pointer.To(true),
AccessAllowedRoles: []string{"os:admin"},
AccessAllowedKubernetesNamespaces: []string{"kube-system"},
},
},
},
})
suite.Require().NoError(suite.state.Create(suite.ctx, cfg))
// worker should have feature disabled even if it is enabled in the config
specMD := resource.NewMetadata(config.NamespaceName, kubeaccess.ConfigType, kubeaccess.ConfigID, resource.VersionUndefined)
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
suite.assertNoResource(specMD)))
}
func TestConfigSuite(t *testing.T) {
suite.Run(t, new(ConfigSuite))
}

View File

@ -0,0 +1,249 @@
// 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 kubeaccess
import (
"context"
"fmt"
"reflect"
"github.com/cosi-project/runtime/pkg/controller"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/state"
"github.com/siderolabs/go-pointer"
"go.uber.org/zap"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"github.com/talos-systems/talos/pkg/kubernetes"
"github.com/talos-systems/talos/pkg/machinery/constants"
"github.com/talos-systems/talos/pkg/machinery/resources/config"
"github.com/talos-systems/talos/pkg/machinery/resources/k8s"
"github.com/talos-systems/talos/pkg/machinery/resources/kubeaccess"
"github.com/talos-systems/talos/pkg/machinery/resources/secrets"
)
// EndpointController manages Kubernetes endpoints resource for Talos API endpoints.
type EndpointController struct{}
// Name implements controller.Controller interface.
func (ctrl *EndpointController) Name() string {
return "kubeaccess.EndpointController"
}
// Inputs implements controller.Controller interface.
func (ctrl *EndpointController) Inputs() []controller.Input {
return nil
}
// Outputs implements controller.Controller interface.
func (ctrl *EndpointController) Outputs() []controller.Output {
return nil
}
// Run implements controller.Controller interface.
//
//nolint:gocyclo
func (ctrl *EndpointController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error {
for {
if err := r.UpdateInputs([]controller.Input{
{
Namespace: config.NamespaceName,
Type: kubeaccess.ConfigType,
ID: pointer.To(kubeaccess.ConfigID),
Kind: controller.InputWeak,
},
}); err != nil {
return err
}
select {
case <-ctx.Done():
return nil
case <-r.EventCh():
}
kubeaccessConfig, err := r.Get(ctx, kubeaccess.NewConfig(config.NamespaceName, kubeaccess.ConfigID).Metadata())
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error fetching kubeaccess config: %w", err)
}
}
if kubeaccessConfig == nil || !kubeaccessConfig.(*kubeaccess.Config).TypedSpec().Enabled {
// disabled, nothing to do
continue
}
if err = ctrl.reconcile(ctx, r, logger); err != nil {
return err
}
}
}
//nolint:gocyclo
func (ctrl *EndpointController) reconcile(ctx context.Context, r controller.Runtime, logger *zap.Logger) error {
if err := r.UpdateInputs([]controller.Input{
{
Namespace: config.NamespaceName,
Type: kubeaccess.ConfigType,
ID: pointer.To(kubeaccess.ConfigID),
Kind: controller.InputWeak,
},
{
Namespace: secrets.NamespaceName,
Type: secrets.KubernetesType,
ID: pointer.To(secrets.KubernetesID),
Kind: controller.InputWeak,
},
{
Namespace: k8s.ControlPlaneNamespaceName,
Type: k8s.EndpointType,
Kind: controller.InputWeak,
},
}); err != nil {
return err
}
r.QueueReconcile()
for {
select {
case <-r.EventCh():
case <-ctx.Done():
return nil
}
kubeaccessConfig, err := r.Get(ctx, kubeaccess.NewConfig(config.NamespaceName, kubeaccess.ConfigID).Metadata())
if err != nil {
if !state.IsNotFoundError(err) {
return fmt.Errorf("error fetching kubeaccess config: %w", err)
}
}
if kubeaccessConfig == nil || !kubeaccessConfig.(*kubeaccess.Config).TypedSpec().Enabled {
// disabled, bail out
return nil
}
endpointResources, err := r.List(ctx, resource.NewMetadata(k8s.ControlPlaneNamespaceName, k8s.EndpointType, "", resource.VersionUndefined))
if err != nil {
return fmt.Errorf("error getting endpoints resources: %w", err)
}
var endpointAddrs k8s.EndpointList
// merge all endpoints into a single list
for _, res := range endpointResources.Items {
endpointAddrs = endpointAddrs.Merge(res.(*k8s.Endpoint))
}
if len(endpointAddrs) == 0 {
continue
}
secretsResources, err := r.Get(ctx, resource.NewMetadata(secrets.NamespaceName, secrets.KubernetesType, secrets.KubernetesID, resource.VersionUndefined))
if err != nil {
if state.IsNotFoundError(err) {
continue
}
return err
}
secrets := secretsResources.(*secrets.Kubernetes).TypedSpec()
kubeconfig, err := clientcmd.BuildConfigFromKubeconfigGetter("", func() (*clientcmdapi.Config, error) {
return clientcmd.Load([]byte(secrets.LocalhostAdminKubeconfig))
})
if err != nil {
return fmt.Errorf("error loading kubeconfig: %w", err)
}
if err = ctrl.updateTalosEndpoints(ctx, logger, kubeconfig, endpointAddrs); err != nil {
return err
}
}
}
//nolint:gocyclo
func (ctrl *EndpointController) updateTalosEndpoints(ctx context.Context, logger *zap.Logger, kubeconfig *rest.Config, endpointAddrs k8s.EndpointList) error {
client, err := kubernetes.NewForConfig(kubeconfig)
if err != nil {
return fmt.Errorf("error building Kubernetes client: %w", err)
}
defer client.Close() //nolint:errcheck
for {
oldEndpoints, err := client.CoreV1().Endpoints(constants.KubernetesTalosAPIServiceNamespace).Get(ctx, constants.KubernetesTalosAPIServiceName, metav1.GetOptions{})
if err != nil && !apierrors.IsNotFound(err) {
return fmt.Errorf("error getting endpoints: %w", err)
}
var newEndpoints *corev1.Endpoints
if apierrors.IsNotFound(err) {
newEndpoints = &corev1.Endpoints{
ObjectMeta: metav1.ObjectMeta{
Name: constants.KubernetesTalosAPIServiceName,
Namespace: constants.KubernetesTalosAPIServiceNamespace,
Labels: map[string]string{
"provider": constants.KubernetesTalosProvider,
"component": "apid",
},
},
}
} else {
newEndpoints = oldEndpoints.DeepCopy()
}
newEndpoints.Subsets = []corev1.EndpointSubset{
{
Ports: []corev1.EndpointPort{
{
Name: "apid",
Port: constants.ApidPort,
Protocol: "TCP",
},
},
},
}
for _, addr := range endpointAddrs {
newEndpoints.Subsets[0].Addresses = append(newEndpoints.Subsets[0].Addresses,
corev1.EndpointAddress{
IP: addr.String(),
},
)
}
if oldEndpoints != nil && reflect.DeepEqual(oldEndpoints.Subsets, newEndpoints.Subsets) {
// no change, bail out
return nil
}
if oldEndpoints == nil {
_, err = client.CoreV1().Endpoints(constants.KubernetesTalosAPIServiceNamespace).Create(ctx, newEndpoints, metav1.CreateOptions{})
} else {
_, err = client.CoreV1().Endpoints(constants.KubernetesTalosAPIServiceNamespace).Update(ctx, newEndpoints, metav1.UpdateOptions{})
}
switch {
case err == nil:
logger.Info("updated Talos API endpoints in Kubernetes", zap.Strings("endpoints", endpointAddrs.Strings()))
return nil
case apierrors.IsConflict(err) || apierrors.IsAlreadyExists(err):
// retry
default:
return fmt.Errorf("error updating Kubernetes Talos API endpoints: %w", err)
}
}
}

View File

@ -0,0 +1,6 @@
// 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 kubeaccess provides controllers which manage Talos API access from Kubernetes workloads.
package kubeaccess

View File

@ -0,0 +1,112 @@
// 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 kubeaccess_test
import (
"context"
"log"
"sync"
"time"
"github.com/cosi-project/runtime/pkg/controller/runtime"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/state"
"github.com/cosi-project/runtime/pkg/state/impl/inmem"
"github.com/cosi-project/runtime/pkg/state/impl/namespaced"
"github.com/stretchr/testify/suite"
"github.com/talos-systems/go-retry/retry"
"github.com/talos-systems/talos/pkg/logging"
"github.com/talos-systems/talos/pkg/machinery/config/types/v1alpha1"
"github.com/talos-systems/talos/pkg/machinery/resources/config"
)
type KubeaccessSuite struct {
suite.Suite
state state.State
runtime *runtime.Runtime
wg sync.WaitGroup
ctx context.Context //nolint:containedctx
ctxCancel context.CancelFunc
}
func (suite *KubeaccessSuite) SetupTest() {
suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 3*time.Minute)
suite.state = state.WrapCore(namespaced.NewState(inmem.Build))
var err error
logger := logging.Wrap(log.Writer())
suite.runtime, err = runtime.NewRuntime(suite.state, logger)
suite.Require().NoError(err)
}
func (suite *KubeaccessSuite) startRuntime() {
suite.wg.Add(1)
go func() {
defer suite.wg.Done()
suite.Assert().NoError(suite.runtime.Run(suite.ctx))
}()
}
func (suite *KubeaccessSuite) assertResource(md resource.Metadata, check func(res resource.Resource) error) func() error {
return func() error {
r, err := suite.state.Get(suite.ctx, md)
if err != nil {
if state.IsNotFoundError(err) {
return retry.ExpectedError(err)
}
return err
}
return check(r)
}
}
func (suite *KubeaccessSuite) assertNoResource(md resource.Metadata) func() error {
return func() error {
_, err := suite.state.Get(suite.ctx, md)
if err == nil {
return retry.ExpectedErrorf("resource %s still exists", md)
}
if state.IsNotFoundError(err) {
return nil
}
return err
}
}
func (suite *KubeaccessSuite) TearDownTest() {
suite.T().Log("tear down")
suite.ctxCancel()
suite.wg.Wait()
// trigger updates in resources to stop watch loops
err := suite.state.Create(
context.Background(), config.NewMachineConfig(
&v1alpha1.Config{
ConfigVersion: "v1alpha1",
MachineConfig: &v1alpha1.MachineConfig{},
},
),
)
if state.IsConflictError(err) {
err = suite.state.Destroy(context.Background(), config.NewMachineConfig(nil).Metadata())
}
suite.Assert().NoError(err)
}

View File

@ -18,6 +18,7 @@ import (
talosconfig "github.com/talos-systems/talos/pkg/machinery/config"
"github.com/talos-systems/talos/pkg/machinery/config/types/v1alpha1/machine"
"github.com/talos-systems/talos/pkg/machinery/constants"
"github.com/talos-systems/talos/pkg/machinery/resources/config"
"github.com/talos-systems/talos/pkg/machinery/resources/secrets"
)
@ -146,6 +147,14 @@ func (ctrl *RootController) updateOSSecrets(cfgProvider talosconfig.Provider, os
}
}
if cfgProvider.Machine().Features().KubernetesTalosAPIAccess().Enabled() {
// add Kubernetes Talos service name to the list of SANs
osSecrets.CertSANDNSNames = append(osSecrets.CertSANDNSNames,
constants.KubernetesTalosAPIServiceName,
constants.KubernetesTalosAPIServiceName+"."+constants.KubernetesTalosAPIServiceNamespace,
)
}
osSecrets.Token = cfgProvider.Machine().Security().Token()
return nil

View File

@ -132,6 +132,7 @@ func (r *Runtime) CanApplyImmediate(cfg config.Provider) error {
// * .machine.kernel
// * .machine.registries (note that auth is not applied immediately, containerd limitation)
// * .machine.pods
// * .machine.features.kubernetesTalosAPIAccess
newConfig.ConfigDebug = currentConfig.ConfigDebug
newConfig.ClusterConfig = currentConfig.ClusterConfig
@ -148,6 +149,10 @@ func (r *Runtime) CanApplyImmediate(cfg config.Provider) error {
newConfig.MachineConfig.MachineKernel = currentConfig.MachineConfig.MachineKernel
newConfig.MachineConfig.MachineRegistries = currentConfig.MachineConfig.MachineRegistries
newConfig.MachineConfig.MachinePods = currentConfig.MachineConfig.MachinePods
if newConfig.MachineConfig.MachineFeatures != nil && currentConfig.MachineConfig.MachineFeatures != nil {
newConfig.MachineConfig.MachineFeatures.KubernetesTalosAPIAccessConfig = currentConfig.MachineConfig.MachineFeatures.KubernetesTalosAPIAccessConfig
}
}
if !reflect.DeepEqual(currentConfig, newConfig) {

View File

@ -25,6 +25,7 @@ import (
"github.com/talos-systems/talos/internal/app/machined/pkg/controllers/files"
"github.com/talos-systems/talos/internal/app/machined/pkg/controllers/hardware"
"github.com/talos-systems/talos/internal/app/machined/pkg/controllers/k8s"
"github.com/talos-systems/talos/internal/app/machined/pkg/controllers/kubeaccess"
"github.com/talos-systems/talos/internal/app/machined/pkg/controllers/kubespan"
"github.com/talos-systems/talos/internal/app/machined/pkg/controllers/network"
"github.com/talos-systems/talos/internal/app/machined/pkg/controllers/perf"
@ -134,6 +135,8 @@ func (ctrl *Controller) Run(ctx context.Context, drainer *runtime.Drainer) error
&k8s.RenderConfigsStaticPodController{},
&k8s.RenderSecretsStaticPodController{},
&k8s.StaticPodConfigController{},
&kubeaccess.ConfigController{},
&kubeaccess.EndpointController{},
&kubespan.ConfigController{},
&kubespan.EndpointController{},
&kubespan.IdentityController{},

View File

@ -20,6 +20,7 @@ import (
"github.com/talos-systems/talos/pkg/machinery/resources/files"
"github.com/talos-systems/talos/pkg/machinery/resources/hardware"
"github.com/talos-systems/talos/pkg/machinery/resources/k8s"
"github.com/talos-systems/talos/pkg/machinery/resources/kubeaccess"
"github.com/talos-systems/talos/pkg/machinery/resources/kubespan"
"github.com/talos-systems/talos/pkg/machinery/resources/network"
"github.com/talos-systems/talos/pkg/machinery/resources/perf"
@ -114,6 +115,7 @@ func NewState() (*State, error) {
&k8s.StaticPod{},
&k8s.StaticPodStatus{},
&k8s.SecretsStatus{},
&kubeaccess.Config{},
&kubespan.Config{},
&kubespan.Endpoint{},
&kubespan.Identity{},

View File

@ -515,6 +515,14 @@ type SystemDiskEncryption interface {
type Features interface {
RBACEnabled() bool
StableHostnameEnabled() bool
KubernetesTalosAPIAccess() KubernetesTalosAPIAccess
}
// KubernetesTalosAPIAccess describes the Kubernetes Talos API access features.
type KubernetesTalosAPIAccess interface {
Enabled() bool
AllowedRoles() []string
AllowedKubernetesNamespaces() []string
}
// VolumeMount describes extra volume mount for the static pods.

View File

@ -59,3 +59,8 @@ func (t *Type) UnmarshalText(text []byte) error {
return err
}
// IsControlPlane returns true if the type is a control plane node.
func (t Type) IsControlPlane() bool {
return t == TypeControlPlane || t == TypeInit
}

View File

@ -4,7 +4,11 @@
package v1alpha1
import "github.com/siderolabs/go-pointer"
import (
"github.com/siderolabs/go-pointer"
"github.com/talos-systems/talos/pkg/machinery/config"
)
// RBACEnabled implements config.Features interface.
func (f *FeaturesConfig) RBACEnabled() bool {
@ -19,3 +23,8 @@ func (f *FeaturesConfig) RBACEnabled() bool {
func (f *FeaturesConfig) StableHostnameEnabled() bool {
return pointer.SafeDeref(f.StableHostname)
}
// KubernetesTalosAPIAccess implements config.Features interface.
func (f *FeaturesConfig) KubernetesTalosAPIAccess() config.KubernetesTalosAPIAccess {
return f.KubernetesTalosAPIAccessConfig
}

View File

@ -0,0 +1,34 @@
// 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 v1alpha1
import "github.com/siderolabs/go-pointer"
// Enabled implements config.KubernetesTalosAPIAccess.
func (c *KubernetesTalosAPIAccessConfig) Enabled() bool {
if c == nil {
return false
}
return pointer.SafeDeref(c.AccessEnabled)
}
// AllowedRoles implements config.KubernetesTalosAPIAccess.
func (c *KubernetesTalosAPIAccessConfig) AllowedRoles() []string {
if c == nil {
return nil
}
return c.AccessAllowedRoles
}
// AllowedKubernetesNamespaces implements config.KubernetesTalosAPIAccess.
func (c *KubernetesTalosAPIAccessConfig) AllowedKubernetesNamespaces() []string {
if c == nil {
return nil
}
return c.AccessAllowedKubernetesNamespaces
}

View File

@ -588,6 +588,16 @@ metadata:
ExtensionImage: "ghcr.io/siderolabs/gvisor:20220117.0-v1.0.0",
},
}
kubernetesTalosAPIAccessConfigExample = &KubernetesTalosAPIAccessConfig{
AccessEnabled: pointer.To(true),
AccessAllowedRoles: []string{
"os:reader",
},
AccessAllowedKubernetesNamespaces: []string{
"kube-system",
},
}
)
// Config defines the v1alpha1 configuration file.
@ -2274,7 +2284,7 @@ type SystemDiskEncryptionConfig struct {
EphemeralPartition *EncryptionConfig `yaml:"ephemeral,omitempty"`
}
// FeaturesConfig describe individual Talos features that can be switched on or off.
// FeaturesConfig describes individual Talos features that can be switched on or off.
type FeaturesConfig struct {
// description: |
// Enable role-based access control (RBAC).
@ -2282,6 +2292,28 @@ type FeaturesConfig struct {
// description: |
// Enable stable default hostname.
StableHostname *bool `yaml:"stableHostname,omitempty"`
// description: |
// Configure Talos API access from Kubernetes pods.
//
// This feature is disabled if the feature config is not specified.
// examples:
// - value: kubernetesTalosAPIAccessConfigExample
KubernetesTalosAPIAccessConfig *KubernetesTalosAPIAccessConfig `yaml:"kubernetesTalosAPIAccess,omitempty"`
}
// KubernetesTalosAPIAccessConfig describes the configuration for the Talos API access from Kubernetes pods.
type KubernetesTalosAPIAccessConfig struct {
// description: |
// Enable Talos API access from Kubernetes pods.
AccessEnabled *bool `yaml:"enabled,omitempty"`
// description: |
// The list of Talos API roles which can be granted for access from Kubernetes pods.
//
// Empty list means that no roles can be granted, so access is blocked.
AccessAllowedRoles []string `yaml:"allowedRoles,omitempty"`
// description: |
// The list of Kubernetes namespaces Talos API access is available from.
AccessAllowedKubernetesNamespaces []string `yaml:"allowedKubernetesNamespaces,omitempty"`
}
// VolumeMountConfig struct describes extra volume mount for the static pods.

View File

@ -69,6 +69,7 @@ var (
RegistryTLSConfigDoc encoder.Doc
SystemDiskEncryptionConfigDoc encoder.Doc
FeaturesConfigDoc encoder.Doc
KubernetesTalosAPIAccessConfigDoc encoder.Doc
VolumeMountConfigDoc encoder.Doc
ClusterInlineManifestDoc encoder.Doc
NetworkKubeSpanDoc encoder.Doc
@ -2240,8 +2241,8 @@ func init() {
SystemDiskEncryptionConfigDoc.Fields[1].Comments[encoder.LineComment] = "Ephemeral partition encryption."
FeaturesConfigDoc.Type = "FeaturesConfig"
FeaturesConfigDoc.Comments[encoder.LineComment] = "FeaturesConfig describe individual Talos features that can be switched on or off."
FeaturesConfigDoc.Description = "FeaturesConfig describe individual Talos features that can be switched on or off."
FeaturesConfigDoc.Comments[encoder.LineComment] = "FeaturesConfig describes individual Talos features that can be switched on or off."
FeaturesConfigDoc.Description = "FeaturesConfig describes individual Talos features that can be switched on or off."
FeaturesConfigDoc.AddExample("", machineFeaturesExample)
FeaturesConfigDoc.AppearsIn = []encoder.Appearance{
@ -2250,7 +2251,7 @@ func init() {
FieldName: "features",
},
}
FeaturesConfigDoc.Fields = make([]encoder.Doc, 2)
FeaturesConfigDoc.Fields = make([]encoder.Doc, 3)
FeaturesConfigDoc.Fields[0].Name = "rbac"
FeaturesConfigDoc.Fields[0].Type = "bool"
FeaturesConfigDoc.Fields[0].Note = ""
@ -2261,6 +2262,41 @@ func init() {
FeaturesConfigDoc.Fields[1].Note = ""
FeaturesConfigDoc.Fields[1].Description = "Enable stable default hostname."
FeaturesConfigDoc.Fields[1].Comments[encoder.LineComment] = "Enable stable default hostname."
FeaturesConfigDoc.Fields[2].Name = "kubernetesTalosAPIAccess"
FeaturesConfigDoc.Fields[2].Type = "KubernetesTalosAPIAccessConfig"
FeaturesConfigDoc.Fields[2].Note = ""
FeaturesConfigDoc.Fields[2].Description = "Configure Talos API access from Kubernetes pods.\n\nThis feature is disabled if the feature config is not specified."
FeaturesConfigDoc.Fields[2].Comments[encoder.LineComment] = "Configure Talos API access from Kubernetes pods."
FeaturesConfigDoc.Fields[2].AddExample("", kubernetesTalosAPIAccessConfigExample)
KubernetesTalosAPIAccessConfigDoc.Type = "KubernetesTalosAPIAccessConfig"
KubernetesTalosAPIAccessConfigDoc.Comments[encoder.LineComment] = "KubernetesTalosAPIAccessConfig describes the configuration for the Talos API access from Kubernetes pods."
KubernetesTalosAPIAccessConfigDoc.Description = "KubernetesTalosAPIAccessConfig describes the configuration for the Talos API access from Kubernetes pods."
KubernetesTalosAPIAccessConfigDoc.AddExample("", kubernetesTalosAPIAccessConfigExample)
KubernetesTalosAPIAccessConfigDoc.AppearsIn = []encoder.Appearance{
{
TypeName: "FeaturesConfig",
FieldName: "kubernetesTalosAPIAccess",
},
}
KubernetesTalosAPIAccessConfigDoc.Fields = make([]encoder.Doc, 3)
KubernetesTalosAPIAccessConfigDoc.Fields[0].Name = "enabled"
KubernetesTalosAPIAccessConfigDoc.Fields[0].Type = "bool"
KubernetesTalosAPIAccessConfigDoc.Fields[0].Note = ""
KubernetesTalosAPIAccessConfigDoc.Fields[0].Description = "Enable Talos API access from Kubernetes pods."
KubernetesTalosAPIAccessConfigDoc.Fields[0].Comments[encoder.LineComment] = "Enable Talos API access from Kubernetes pods."
KubernetesTalosAPIAccessConfigDoc.Fields[1].Name = "allowedRoles"
KubernetesTalosAPIAccessConfigDoc.Fields[1].Type = "[]string"
KubernetesTalosAPIAccessConfigDoc.Fields[1].Note = ""
KubernetesTalosAPIAccessConfigDoc.Fields[1].Description = "The list of Talos API roles which can be granted for access from Kubernetes pods.\n\nEmpty list means that no roles can be granted, so access is blocked."
KubernetesTalosAPIAccessConfigDoc.Fields[1].Comments[encoder.LineComment] = "The list of Talos API roles which can be granted for access from Kubernetes pods."
KubernetesTalosAPIAccessConfigDoc.Fields[2].Name = "allowedKubernetesNamespaces"
KubernetesTalosAPIAccessConfigDoc.Fields[2].Type = "[]string"
KubernetesTalosAPIAccessConfigDoc.Fields[2].Note = ""
KubernetesTalosAPIAccessConfigDoc.Fields[2].Description = "The list of Kubernetes namespaces Talos API access is available from."
KubernetesTalosAPIAccessConfigDoc.Fields[2].Comments[encoder.LineComment] = "The list of Kubernetes namespaces Talos API access is available from."
VolumeMountConfigDoc.Type = "VolumeMountConfig"
VolumeMountConfigDoc.Comments[encoder.LineComment] = "VolumeMountConfig struct describes extra volume mount for the static pods."
@ -2781,6 +2817,10 @@ func (_ FeaturesConfig) Doc() *encoder.Doc {
return &FeaturesConfigDoc
}
func (_ KubernetesTalosAPIAccessConfig) Doc() *encoder.Doc {
return &KubernetesTalosAPIAccessConfigDoc
}
func (_ VolumeMountConfig) Doc() *encoder.Doc {
return &VolumeMountConfigDoc
}
@ -2894,6 +2934,7 @@ func GetConfigurationDoc() *encoder.FileDoc {
&RegistryTLSConfigDoc,
&SystemDiskEncryptionConfigDoc,
&FeaturesConfigDoc,
&KubernetesTalosAPIAccessConfigDoc,
&VolumeMountConfigDoc,
&ClusterInlineManifestDoc,
&NetworkKubeSpanDoc,

View File

@ -254,6 +254,14 @@ func (c *Config) Validate(mode config.RuntimeMode, options ...config.ValidationO
}
}
if c.Machine().Features().KubernetesTalosAPIAccess().Enabled() && !c.Machine().Features().RBACEnabled() {
result = multierror.Append(result, fmt.Errorf("feature API RBAC should be enabled when Kubernetes Talos API Access feature is enabled"))
}
if c.Machine().Features().KubernetesTalosAPIAccess().Enabled() && !c.Machine().Type().IsControlPlane() {
result = multierror.Append(result, fmt.Errorf("feature Kubernetes Talos API Access can only be enabled on control plane machines"))
}
if opts.Strict {
for _, w := range warnings {
result = multierror.Append(result, fmt.Errorf("warning: %s", w))

View File

@ -1138,6 +1138,51 @@ func TestValidate(t *testing.T) {
},
expectedError: "1 error occurred:\n\t* [networking.os.device.deviceSelector]: config section should contain at least one field\n\n",
},
{
name: "TalosAPIAccessRBAC",
config: &v1alpha1.Config{
ConfigVersion: "v1alpha1",
MachineConfig: &v1alpha1.MachineConfig{
MachineType: "controlplane",
MachineFeatures: &v1alpha1.FeaturesConfig{
KubernetesTalosAPIAccessConfig: &v1alpha1.KubernetesTalosAPIAccessConfig{
AccessEnabled: pointer.To(true),
},
},
},
ClusterConfig: &v1alpha1.ClusterConfig{
ControlPlane: &v1alpha1.ControlPlaneConfig{
Endpoint: &v1alpha1.Endpoint{
endpointURL,
},
},
},
},
expectedError: "1 error occurred:\n\t* feature API RBAC should be enabled when Kubernetes Talos API Access feature is enabled\n\n",
},
{
name: "TalosAPIAccessWorker",
config: &v1alpha1.Config{
ConfigVersion: "v1alpha1",
MachineConfig: &v1alpha1.MachineConfig{
MachineType: "worker",
MachineFeatures: &v1alpha1.FeaturesConfig{
RBAC: pointer.To(true),
KubernetesTalosAPIAccessConfig: &v1alpha1.KubernetesTalosAPIAccessConfig{
AccessEnabled: pointer.To(true),
},
},
},
ClusterConfig: &v1alpha1.ClusterConfig{
ControlPlane: &v1alpha1.ControlPlaneConfig{
Endpoint: &v1alpha1.Endpoint{
endpointURL,
},
},
},
},
expectedError: "1 error occurred:\n\t* feature Kubernetes Talos API Access can only be enabled on control plane machines\n\n",
},
} {
test := test

View File

@ -927,6 +927,11 @@ func (in *FeaturesConfig) DeepCopyInto(out *FeaturesConfig) {
*out = new(bool)
**out = **in
}
if in.KubernetesTalosAPIAccessConfig != nil {
in, out := &in.KubernetesTalosAPIAccessConfig, &out.KubernetesTalosAPIAccessConfig
*out = new(KubernetesTalosAPIAccessConfig)
(*in).DeepCopyInto(*out)
}
return
}
@ -1142,6 +1147,37 @@ func (in *KubeletNodeIPConfig) DeepCopy() *KubeletNodeIPConfig {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KubernetesTalosAPIAccessConfig) DeepCopyInto(out *KubernetesTalosAPIAccessConfig) {
*out = *in
if in.AccessEnabled != nil {
in, out := &in.AccessEnabled, &out.AccessEnabled
*out = new(bool)
**out = **in
}
if in.AccessAllowedRoles != nil {
in, out := &in.AccessAllowedRoles, &out.AccessAllowedRoles
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.AccessAllowedKubernetesNamespaces != nil {
in, out := &in.AccessAllowedKubernetesNamespaces, &out.AccessAllowedKubernetesNamespaces
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesTalosAPIAccessConfig.
func (in *KubernetesTalosAPIAccessConfig) DeepCopy() *KubernetesTalosAPIAccessConfig {
if in == nil {
return nil
}
out := new(KubernetesTalosAPIAccessConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *LoggingConfig) DeepCopyInto(out *LoggingConfig) {
*out = *in

View File

@ -689,6 +689,15 @@ const (
// GoVersion is the version of Go compiler this release was built with.
GoVersion = "go1.18.4"
// KubernetesTalosAPIServiceName is the name of the Kubernetes service to access Talos API.
KubernetesTalosAPIServiceName = "talos"
// KubernetesTalosAPIServiceNamespace is the namespace of the Kubernetes service to access Talos API.
KubernetesTalosAPIServiceNamespace = "default"
// KubernetesTalosProvider is the name of the Talos provider as a Kubernetes label.
KubernetesTalosProvider = "talos.dev"
)
// See https://linux.die.net/man/3/klogctl

View File

@ -44,6 +44,8 @@ type BootstrapManifestsConfigSpec struct {
FlannelCNIImage string `yaml:"flannelCNIImage"`
PodSecurityPolicyEnabled bool `yaml:"podSecurityPolicyEnabled"`
TalosAPIServiceEnabled bool `yaml:"talosAPIServiceEnabled"`
}
// NewBootstrapManifestsConfig returns new BootstrapManifestsConfig resource.

View File

@ -0,0 +1,68 @@
// 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 kubeaccess
import (
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/resource/meta"
"github.com/cosi-project/runtime/pkg/resource/typed"
"github.com/talos-systems/talos/pkg/machinery/resources/config"
)
// ConfigType is type of Config resource.
const ConfigType = resource.Type("KubernetesAccessConfigs.cluster.talos.dev")
// ConfigID the singleton config resource ID.
const ConfigID = resource.ID("config")
// Config resource holds KubeSpan configuration.
type Config = typed.Resource[ConfigSpec, ConfigRD]
// ConfigSpec describes KubeSpan configuration..
type ConfigSpec struct {
Enabled bool `yaml:"enabled"`
AllowedAPIRoles []string `yaml:"allowedAPIRoles"`
AllowedKubernetesNamespaces []string `yaml:"allowedKubernetesNamespaces"`
}
// DeepCopy generates a deep copy of ConfigSpec.
func (cs ConfigSpec) DeepCopy() ConfigSpec {
cp := cs
if cs.AllowedAPIRoles != nil {
cp.AllowedAPIRoles = make([]string, len(cs.AllowedAPIRoles))
copy(cp.AllowedAPIRoles, cs.AllowedAPIRoles)
}
if cs.AllowedKubernetesNamespaces != nil {
cp.AllowedKubernetesNamespaces = make([]string, len(cs.AllowedKubernetesNamespaces))
copy(cp.AllowedKubernetesNamespaces, cs.AllowedKubernetesNamespaces)
}
return cp
}
// NewConfig initializes a Config resource.
func NewConfig(namespace resource.Namespace, id resource.ID) *Config {
return typed.NewResource[ConfigSpec, ConfigRD](
resource.NewMetadata(namespace, ConfigType, id, resource.VersionUndefined),
ConfigSpec{},
)
}
// ConfigRD provides auxiliary methods for Config.
type ConfigRD struct{}
// ResourceDefinition implements typed.ResourceDefinition interface.
func (c ConfigRD) ResourceDefinition(resource.Metadata, ConfigSpec) meta.ResourceDefinitionSpec {
return meta.ResourceDefinitionSpec{
Type: ConfigType,
Aliases: []resource.Type{},
DefaultNamespace: config.NamespaceName,
PrintColumns: []meta.PrintColumn{},
Sensitivity: meta.NonSensitive,
}
}

View File

@ -0,0 +1,6 @@
// 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 kubeaccess provides resources related to the Talos API access from Kubernetes workloads.
package kubeaccess

View File

@ -0,0 +1,32 @@
// 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 kubeaccess_test
import (
"context"
"testing"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/state"
"github.com/cosi-project/runtime/pkg/state/impl/inmem"
"github.com/cosi-project/runtime/pkg/state/impl/namespaced"
"github.com/cosi-project/runtime/pkg/state/registry"
"github.com/stretchr/testify/assert"
"github.com/talos-systems/talos/pkg/machinery/resources/kubeaccess"
)
func TestRegisterResource(t *testing.T) {
ctx := context.TODO()
resources := state.WrapCore(namespaced.NewState(inmem.Build))
resourceRegistry := registry.NewResourceRegistry(resources)
for _, resource := range []resource.Resource{
&kubeaccess.Config{},
} {
assert.NoError(t, resourceRegistry.Register(ctx, resource))
}
}

View File

@ -363,6 +363,16 @@ systemDiskEncryption:
|`features` |<a href="#featuresconfig">FeaturesConfig</a> |Features describe individual Talos features that can be switched on or off. <details><summary>Show example(s)</summary>{{< highlight yaml >}}
features:
rbac: true # Enable role-based access control (RBAC).
# # Configure Talos API access from Kubernetes pods.
# kubernetesTalosAPIAccess:
# enabled: true # Enable Talos API access from Kubernetes pods.
# # The list of Talos API roles which can be granted for access from Kubernetes pods.
# allowedRoles:
# - os:reader
# # The list of Kubernetes namespaces Talos API access is available from.
# allowedKubernetesNamespaces:
# - kube-system
{{< /highlight >}}</details> | |
|`udev` |<a href="#udevconfig">UdevConfig</a> |Configures the udev system. <details><summary>Show example(s)</summary>{{< highlight yaml >}}
udev:
@ -2513,7 +2523,7 @@ ephemeral:
---
## FeaturesConfig
FeaturesConfig describe individual Talos features that can be switched on or off.
FeaturesConfig describes individual Talos features that can be switched on or off.
Appears in:
@ -2523,6 +2533,16 @@ Appears in:
{{< highlight yaml >}}
rbac: true # Enable role-based access control (RBAC).
# # Configure Talos API access from Kubernetes pods.
# kubernetesTalosAPIAccess:
# enabled: true # Enable Talos API access from Kubernetes pods.
# # The list of Talos API roles which can be granted for access from Kubernetes pods.
# allowedRoles:
# - os:reader
# # The list of Kubernetes namespaces Talos API access is available from.
# allowedKubernetesNamespaces:
# - kube-system
{{< /highlight >}}
@ -2530,6 +2550,45 @@ rbac: true # Enable role-based access control (RBAC).
|-------|------|-------------|----------|
|`rbac` |bool |Enable role-based access control (RBAC). | |
|`stableHostname` |bool |Enable stable default hostname. | |
|`kubernetesTalosAPIAccess` |<a href="#kubernetestalosapiaccessconfig">KubernetesTalosAPIAccessConfig</a> |<details><summary>Configure Talos API access from Kubernetes pods.</summary><br />This feature is disabled if the feature config is not specified.</details> <details><summary>Show example(s)</summary>{{< highlight yaml >}}
kubernetesTalosAPIAccess:
enabled: true # Enable Talos API access from Kubernetes pods.
# The list of Talos API roles which can be granted for access from Kubernetes pods.
allowedRoles:
- os:reader
# The list of Kubernetes namespaces Talos API access is available from.
allowedKubernetesNamespaces:
- kube-system
{{< /highlight >}}</details> | |
---
## KubernetesTalosAPIAccessConfig
KubernetesTalosAPIAccessConfig describes the configuration for the Talos API access from Kubernetes pods.
Appears in:
- <code><a href="#featuresconfig">FeaturesConfig</a>.kubernetesTalosAPIAccess</code>
{{< highlight yaml >}}
enabled: true # Enable Talos API access from Kubernetes pods.
# The list of Talos API roles which can be granted for access from Kubernetes pods.
allowedRoles:
- os:reader
# The list of Kubernetes namespaces Talos API access is available from.
allowedKubernetesNamespaces:
- kube-system
{{< /highlight >}}
| Field | Type | Description | Value(s) |
|-------|------|-------------|----------|
|`enabled` |bool |Enable Talos API access from Kubernetes pods. | |
|`allowedRoles` |[]string |<details><summary>The list of Talos API roles which can be granted for access from Kubernetes pods.</summary><br />Empty list means that no roles can be granted, so access is blocked.</details> | |
|`allowedKubernetesNamespaces` |[]string |The list of Kubernetes namespaces Talos API access is available from. | |

View File

@ -47,6 +47,7 @@ The list of config changes allowed to be applied immediately in Talos {{< releas
* `.machine.pods`
* `.machine.kernel`
* `.machine.registries` (CRI containerd plugin will not pick up the registry authentication settings without a reboot)
* `.machine.features.kubernetesTalosAPIAccess`
### `talosctl apply-config`