fix: derive machine-id from node identity

Fixes #4759

This uses existing features: Talos always generates 32 bytes random node
identity, we use first 16 bytes of that to generate `machine-id` in
compliant format and mount that into the `kubelet` container.

Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
This commit is contained in:
Andrey Smirnov 2022-01-10 15:57:26 +03:00
parent d8a2721e12
commit 2e735714d9
No known key found for this signature in database
GPG Key ID: 7B26396447AB6DFD
7 changed files with 78 additions and 7 deletions

View File

@ -377,9 +377,7 @@ COPY ./hack/cleanup.sh /toolchain/bin/cleanup.sh
RUN cleanup.sh /rootfs RUN cleanup.sh /rootfs
COPY --chmod=0644 hack/containerd.toml /rootfs/etc/containerd/config.toml COPY --chmod=0644 hack/containerd.toml /rootfs/etc/containerd/config.toml
COPY --chmod=0644 hack/cri-containerd.toml /rootfs/etc/cri/containerd.toml COPY --chmod=0644 hack/cri-containerd.toml /rootfs/etc/cri/containerd.toml
RUN touch /rootfs/etc/resolv.conf RUN touch /rootfs/etc/{resolv.conf,hosts,os-release,machine-id}
RUN touch /rootfs/etc/hosts
RUN touch /rootfs/etc/os-release
RUN mkdir -pv /rootfs/{boot,usr/local/share,mnt,system,opt} RUN mkdir -pv /rootfs/{boot,usr/local/share,mnt,system,opt}
RUN mkdir -pv /rootfs/{etc/kubernetes/manifests,etc/cni/net.d,usr/libexec/kubernetes} RUN mkdir -pv /rootfs/{etc/kubernetes/manifests,etc/cni/net.d,usr/libexec/kubernetes}
RUN mkdir -pv /rootfs/opt/{containerd/bin,containerd/lib} RUN mkdir -pv /rootfs/opt/{containerd/bin,containerd/lib}
@ -421,9 +419,7 @@ COPY ./hack/cleanup.sh /toolchain/bin/cleanup.sh
RUN cleanup.sh /rootfs RUN cleanup.sh /rootfs
COPY --chmod=0644 hack/containerd.toml /rootfs/etc/containerd/containerd.toml COPY --chmod=0644 hack/containerd.toml /rootfs/etc/containerd/containerd.toml
COPY --chmod=0644 hack/cri-containerd.toml /rootfs/etc/cri/containerd.toml COPY --chmod=0644 hack/cri-containerd.toml /rootfs/etc/cri/containerd.toml
RUN touch /rootfs/etc/resolv.conf RUN touch /rootfs/etc/{resolv.conf,hosts,os-release,machine-id}
RUN touch /rootfs/etc/hosts
RUN touch /rootfs/etc/os-release
RUN mkdir -pv /rootfs/{boot,usr/local/share,mnt,system,opt} RUN mkdir -pv /rootfs/{boot,usr/local/share,mnt,system,opt}
RUN mkdir -pv /rootfs/{etc/kubernetes/manifests,etc/cni/net.d,usr/libexec/kubernetes} RUN mkdir -pv /rootfs/{etc/kubernetes/manifests,etc/cni/net.d,usr/libexec/kubernetes}
RUN mkdir -pv /rootfs/opt/{containerd/bin,containerd/lib} RUN mkdir -pv /rootfs/opt/{containerd/bin,containerd/lib}

View File

@ -6,6 +6,7 @@ package cluster
import ( import (
"crypto/rand" "crypto/rand"
"encoding/hex"
"io" "io"
"github.com/jxskiss/base62" "github.com/jxskiss/base62"
@ -39,3 +40,16 @@ func (a identity) Generate() error {
return nil return nil
} }
// ConvertMachineID returns /etc/machine-id compatible representation.
func (a identity) ConvertMachineID() ([]byte, error) {
raw, err := base62.DecodeString(a.IdentitySpec.NodeID)
if err != nil {
return nil, err
}
buf := make([]byte, 32)
hex.Encode(buf, raw[:16])
return buf, nil
}

View File

@ -27,3 +27,14 @@ func TestIdentityGenerate(t *testing.T) {
assert.GreaterOrEqual(t, length, 43) assert.GreaterOrEqual(t, length, 43)
assert.LessOrEqual(t, length, 44) assert.LessOrEqual(t, length, 44)
} }
func TestIdentityConvertMachineID(t *testing.T) {
spec := cluster.IdentitySpec{
NodeID: "sou7yy34ykX3n373Zw1DXKb8zD7UnyKT6HT3QDsGH6L",
}
machineID, err := clusteradapter.IdentitySpec(&spec).ConvertMachineID()
require.NoError(t, err)
assert.Equal(t, "be871ac0d0dd31fa4caca753b0f3f1b2", string(machineID))
}

View File

@ -20,6 +20,7 @@ import (
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime" "github.com/talos-systems/talos/internal/app/machined/pkg/runtime"
"github.com/talos-systems/talos/pkg/machinery/constants" "github.com/talos-systems/talos/pkg/machinery/constants"
"github.com/talos-systems/talos/pkg/machinery/resources/cluster" "github.com/talos-systems/talos/pkg/machinery/resources/cluster"
"github.com/talos-systems/talos/pkg/machinery/resources/files"
runtimeres "github.com/talos-systems/talos/pkg/machinery/resources/runtime" runtimeres "github.com/talos-systems/talos/pkg/machinery/resources/runtime"
"github.com/talos-systems/talos/pkg/machinery/resources/v1alpha1" "github.com/talos-systems/talos/pkg/machinery/resources/v1alpha1"
) )
@ -56,6 +57,10 @@ func (ctrl *NodeIdentityController) Outputs() []controller.Output {
Type: cluster.IdentityType, Type: cluster.IdentityType,
Kind: controller.OutputShared, Kind: controller.OutputShared,
}, },
{
Type: files.EtcFileSpecType,
Kind: controller.OutputShared,
},
} }
} }
@ -102,6 +107,19 @@ func (ctrl *NodeIdentityController) Run(ctx context.Context, r controller.Runtim
return fmt.Errorf("error modifying resource: %w", err) return fmt.Errorf("error modifying resource: %w", err)
} }
// generate `/etc/machine-id` from node identity
if err := r.Modify(ctx, files.NewEtcFileSpec(files.NamespaceName, "machine-id"),
func(r resource.Resource) error {
var err error
r.(*files.EtcFileSpec).TypedSpec().Contents, err = clusteradapter.IdentitySpec(&localIdentity).ConvertMachineID()
r.(*files.EtcFileSpec).TypedSpec().Mode = 0o444
return err
}); err != nil {
return fmt.Errorf("error modifying resolv.conf: %w", err)
}
if !ctrl.identityEstablished { if !ctrl.identityEstablished {
logger.Info("node identity established", zap.String("node_id", localIdentity.NodeID)) logger.Info("node identity established", zap.String("node_id", localIdentity.NodeID))

View File

@ -19,6 +19,7 @@ import (
v1alpha1runtime "github.com/talos-systems/talos/internal/app/machined/pkg/runtime" v1alpha1runtime "github.com/talos-systems/talos/internal/app/machined/pkg/runtime"
"github.com/talos-systems/talos/pkg/machinery/constants" "github.com/talos-systems/talos/pkg/machinery/constants"
"github.com/talos-systems/talos/pkg/machinery/resources/cluster" "github.com/talos-systems/talos/pkg/machinery/resources/cluster"
"github.com/talos-systems/talos/pkg/machinery/resources/files"
runtimeres "github.com/talos-systems/talos/pkg/machinery/resources/runtime" runtimeres "github.com/talos-systems/talos/pkg/machinery/resources/runtime"
"github.com/talos-systems/talos/pkg/machinery/resources/v1alpha1" "github.com/talos-systems/talos/pkg/machinery/resources/v1alpha1"
) )
@ -68,6 +69,12 @@ func (suite *NodeIdentitySuite) TestDefault() {
return nil return nil
}), }),
)) ))
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
suite.assertResource(*files.NewEtcFileSpec(files.NamespaceName, "machine-id").Metadata(), func(_ resource.Resource) error {
return nil
}),
))
} }
func (suite *NodeIdentitySuite) TestLoad() { func (suite *NodeIdentitySuite) TestLoad() {
@ -93,6 +100,14 @@ func (suite *NodeIdentitySuite) TestLoad() {
return nil return nil
}), }),
)) ))
suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
suite.assertResource(*files.NewEtcFileSpec(files.NamespaceName, "machine-id").Metadata(), func(r resource.Resource) error {
suite.Assert().Equal("8d2c0de2408fa2a178bad7f45d9aa8fb", string(r.(*files.EtcFileSpec).TypedSpec().Contents))
return nil
}),
))
} }
func TestNodeIdentitySuite(t *testing.T) { func TestNodeIdentitySuite(t *testing.T) {

View File

@ -26,6 +26,7 @@ import (
"github.com/talos-systems/talos/internal/app/machined/pkg/system" "github.com/talos-systems/talos/internal/app/machined/pkg/system"
"github.com/talos-systems/talos/internal/app/machined/pkg/system/services" "github.com/talos-systems/talos/internal/app/machined/pkg/system/services"
"github.com/talos-systems/talos/pkg/machinery/constants" "github.com/talos-systems/talos/pkg/machinery/constants"
"github.com/talos-systems/talos/pkg/machinery/resources/files"
"github.com/talos-systems/talos/pkg/machinery/resources/k8s" "github.com/talos-systems/talos/pkg/machinery/resources/k8s"
"github.com/talos-systems/talos/pkg/machinery/resources/secrets" "github.com/talos-systems/talos/pkg/machinery/resources/secrets"
"github.com/talos-systems/talos/pkg/machinery/resources/v1alpha1" "github.com/talos-systems/talos/pkg/machinery/resources/v1alpha1"
@ -63,7 +64,7 @@ func (ctrl *KubeletServiceController) Outputs() []controller.Output {
// //
//nolint:gocyclo,cyclop //nolint:gocyclo,cyclop
func (ctrl *KubeletServiceController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { func (ctrl *KubeletServiceController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error {
// initially, wait for the cri to be up // initially, wait for the cri to be up and for machine-id to be generated
if err := r.UpdateInputs([]controller.Input{ if err := r.UpdateInputs([]controller.Input{
{ {
Namespace: v1alpha1.NamespaceName, Namespace: v1alpha1.NamespaceName,
@ -71,6 +72,12 @@ func (ctrl *KubeletServiceController) Run(ctx context.Context, r controller.Runt
ID: pointer.ToString("cri"), ID: pointer.ToString("cri"),
Kind: controller.InputWeak, Kind: controller.InputWeak,
}, },
{
Namespace: files.NamespaceName,
Type: files.EtcFileStatusType,
ID: pointer.ToString("machine-id"),
Kind: controller.InputWeak,
},
}); err != nil { }); err != nil {
return err return err
} }
@ -82,6 +89,15 @@ func (ctrl *KubeletServiceController) Run(ctx context.Context, r controller.Runt
case <-r.EventCh(): case <-r.EventCh():
} }
_, err := r.Get(ctx, resource.NewMetadata(files.NamespaceName, files.EtcFileStatusType, "machine-id", resource.VersionUndefined))
if err != nil {
if state.IsNotFoundError(err) {
continue
}
return fmt.Errorf("error getting etc file status: %w", err)
}
svc, err := r.Get(ctx, resource.NewMetadata(v1alpha1.NamespaceName, v1alpha1.ServiceType, "cri", resource.VersionUndefined)) svc, err := r.Get(ctx, resource.NewMetadata(v1alpha1.NamespaceName, v1alpha1.ServiceType, "cri", resource.VersionUndefined))
if err != nil { if err != nil {
if state.IsNotFoundError(err) { if state.IsNotFoundError(err) {

View File

@ -108,6 +108,7 @@ func (k *Kubelet) Runner(r runtime.Runtime) (runner.Runner, error) {
{Type: "bind", Destination: constants.CgroupMountPath, Source: constants.CgroupMountPath, Options: []string{"rbind", "rshared", "rw"}}, {Type: "bind", Destination: constants.CgroupMountPath, Source: constants.CgroupMountPath, Options: []string{"rbind", "rshared", "rw"}},
{Type: "bind", Destination: "/lib/modules", Source: "/lib/modules", Options: []string{"bind", "ro"}}, {Type: "bind", Destination: "/lib/modules", Source: "/lib/modules", Options: []string{"bind", "ro"}},
{Type: "bind", Destination: "/etc/kubernetes", Source: "/etc/kubernetes", Options: []string{"bind", "rshared", "rw"}}, {Type: "bind", Destination: "/etc/kubernetes", Source: "/etc/kubernetes", Options: []string{"bind", "rshared", "rw"}},
{Type: "bind", Destination: "/etc/machine-id", Source: "/etc/machine-id", Options: []string{"bind", "ro"}},
{Type: "bind", Destination: "/etc/os-release", Source: "/etc/os-release", Options: []string{"bind", "ro"}}, {Type: "bind", Destination: "/etc/os-release", Source: "/etc/os-release", Options: []string{"bind", "ro"}},
{Type: "bind", Destination: "/etc/cni", Source: "/etc/cni", Options: []string{"rbind", "rshared", "rw"}}, {Type: "bind", Destination: "/etc/cni", Source: "/etc/cni", Options: []string{"rbind", "rshared", "rw"}},
{Type: "bind", Destination: "/usr/libexec/kubernetes", Source: "/usr/libexec/kubernetes", Options: []string{"rbind", "rshared", "rw"}}, {Type: "bind", Destination: "/usr/libexec/kubernetes", Source: "/usr/libexec/kubernetes", Options: []string{"rbind", "rshared", "rw"}},