Laura Brehm 7f2eb48561
feat: add image verification endpoint
Add support for whole machine-wide image verification configuration.
Configuration is a set of rules applied top-down to the image reference,
each specifying a specific cosign-based identity or static public key
claim.

Talos provides a machined API to verify an image reference, resolving it
to the digest on the way as needed.

Talos itself hooks up in the image verification process, while
containerd CRI plugin accesses same API via the machined socket.

Signed-off-by: Laura Brehm <laurabrehm@hey.com>
Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
2026-03-06 20:06:07 +04:00

122 lines
3.4 KiB
Go

// 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 install
import (
"context"
"errors"
"fmt"
containerd "github.com/containerd/containerd/v2/client"
"github.com/containerd/containerd/v2/pkg/cio"
"github.com/containerd/containerd/v2/pkg/namespaces"
"github.com/containerd/containerd/v2/pkg/oci"
"github.com/containerd/errdefs"
"github.com/cosi-project/runtime/pkg/state"
"github.com/siderolabs/talos/internal/pkg/containers/image"
"github.com/siderolabs/talos/internal/pkg/containers/image/console"
"github.com/siderolabs/talos/internal/pkg/selinux"
"github.com/siderolabs/talos/pkg/machinery/constants"
)
// PullAndValidateInstallerImage pulls down the installer and validates that it can run.
//
//nolint:gocyclo
func PullAndValidateInstallerImage(ctx context.Context, resources state.State, registryBuilder image.RegistriesBuilder, ref string) error {
// Pull down specified installer image early so we can bail if it doesn't exist in the upstream registry
containerdctx := namespaces.WithNamespace(ctx, constants.SystemContainerdNamespace)
const containerID = "validate"
client, err := containerd.New(constants.SystemContainerdAddress)
if err != nil {
return err
}
defer client.Close() //nolint:errcheck
img, err := image.Pull(containerdctx, registryBuilder, resources, client, ref,
image.WithSkipIfAlreadyPulled(),
image.WithMaxNotFoundRetries(1),
image.WithProgressReporter(console.NewProgressReporter),
)
if err != nil {
return err
}
// See if there's previous container/snapshot to clean up
var oldcontainer containerd.Container
if oldcontainer, err = client.LoadContainer(containerdctx, containerID); err == nil {
if err = oldcontainer.Delete(containerdctx, containerd.WithSnapshotCleanup); err != nil {
return fmt.Errorf("error deleting old container instance: %w", err)
}
}
if err = client.SnapshotService("").Remove(containerdctx, containerID); err != nil && !errdefs.IsNotFound(err) {
return fmt.Errorf("error cleaning up stale snapshot: %w", err)
}
// Launch the container with a known help command for a simple check to make sure the image is valid
args := []string{
"/bin/installer",
"--help",
}
specOpts := []oci.SpecOpts{
oci.WithImageConfig(img),
oci.WithProcessArgs(args...),
}
if selinux.IsEnabled() {
specOpts = append(specOpts, oci.WithSelinuxLabel(constants.SelinuxLabelInstaller))
}
containerOpts := []containerd.NewContainerOpts{
containerd.WithImage(img),
containerd.WithNewSnapshot(containerID, img),
containerd.WithNewSpec(specOpts...),
}
container, err := client.NewContainer(containerdctx, containerID, containerOpts...)
if err != nil {
return err
}
//nolint:errcheck
defer container.Delete(containerdctx, containerd.WithSnapshotCleanup)
task, err := container.NewTask(containerdctx, cio.NullIO)
if err != nil {
return err
}
//nolint:errcheck
defer task.Delete(containerdctx)
exitStatusC, err := task.Wait(containerdctx)
if err != nil {
return err
}
if err = task.Start(containerdctx); err != nil {
return err
}
status := <-exitStatusC
code, _, err := status.Result()
if err != nil {
return err
}
if code != 0 {
return errors.New("installer help returned non-zero exit. assuming invalid installer")
}
return nil
}