mirror of
https://github.com/siderolabs/talos.git
synced 2026-05-05 20:36:18 +02:00
fix: don't set xattrs while decompressing extensions
When decompressing extensions, we might not be able to set xattrs (e.g. running rootless), so instead of setting xattrs, save them in memory and push to mksquashfs as pseudo definitions. Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com> (cherry picked from commit d697f5538a7a624a1ac7bafdfebc67dd9418c434)
This commit is contained in:
parent
9cc735588b
commit
9be7bc0250
@ -12,6 +12,8 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/siderolabs/talos/pkg/machinery/imager/quirks"
|
||||
)
|
||||
@ -47,7 +49,7 @@ func initramfsPaths(quirks quirks.Quirks) []string {
|
||||
//
|
||||
// Components which should be placed to the initramfs are moved to the initramfsPath.
|
||||
// Ucode components are moved into a separate designated location.
|
||||
func (ext *Extension) Compress(ctx context.Context, squashPath, initramfsPath string, quirks quirks.Quirks) (string, error) {
|
||||
func (ext *Extension) Compress(ctx context.Context, squashPath, initramfsPath string, quirks quirks.Quirks, xattrsMap map[string]string) (string, error) {
|
||||
if err := ext.handleUcode(initramfsPath, quirks); err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -70,12 +72,62 @@ func (ext *Extension) Compress(ctx context.Context, squashPath, initramfsPath st
|
||||
compressArgs = []string{"-comp", "xz", "-Xdict-size", "100%"}
|
||||
}
|
||||
|
||||
cmd := exec.CommandContext(ctx, "mksquashfs", append([]string{ext.RootfsPath(), squashPath, "-all-root", "-noappend", "-no-progress"}, compressArgs...)...)
|
||||
pseudoFlags, err := ext.xattrPseudoFlags(xattrsMap)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
cmd := exec.CommandContext(ctx, "mksquashfs",
|
||||
slices.Concat(
|
||||
[]string{
|
||||
ext.RootfsPath(),
|
||||
squashPath,
|
||||
"-all-root",
|
||||
"-noappend",
|
||||
"-no-progress",
|
||||
},
|
||||
compressArgs,
|
||||
pseudoFlags,
|
||||
)...)
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
return squashPath, cmd.Run()
|
||||
}
|
||||
|
||||
// xattrPseudoFlags returns a list of pseudo-flag strings for the xattrs of the extension.
|
||||
//
|
||||
// These pseudo-flags are used to indicate the presence of specific SELinux xattrs on files within the extension.
|
||||
// The mksquashfs tool will use that to mark files with xattrs instead of reading it from the filesystem.
|
||||
func (ext *Extension) xattrPseudoFlags(xattrsMap map[string]string) ([]string, error) {
|
||||
if xattrsMap == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
flags := []string{"-xattrs-exclude", ".*"} // exclude all xattrs by default
|
||||
|
||||
for path, xattrValue := range xattrsMap {
|
||||
if strings.HasPrefix(path, ext.RootfsPath()) {
|
||||
// check if the file exists still (it might have been moved to the initramfs)
|
||||
if _, err := os.Lstat(path); os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
|
||||
relativePath, err := filepath.Rel(ext.RootfsPath(), path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if relativePath == "." {
|
||||
relativePath = "/"
|
||||
}
|
||||
|
||||
flags = append(flags, "-p", fmt.Sprintf("%s x security.selinux=%s", relativePath, xattrValue))
|
||||
}
|
||||
}
|
||||
|
||||
return flags, nil
|
||||
}
|
||||
|
||||
func appendBlob(dst io.Writer, srcPath string) error {
|
||||
src, err := os.Open(srcPath)
|
||||
if err != nil {
|
||||
|
||||
@ -30,7 +30,7 @@ func TestCompress(t *testing.T) {
|
||||
ext := exts[0]
|
||||
|
||||
squashDest, initramfsDest := t.TempDir(), t.TempDir()
|
||||
squashFile, err := ext.Compress(t.Context(), squashDest, initramfsDest, quirks.New(""))
|
||||
squashFile, err := ext.Compress(t.Context(), squashDest, initramfsDest, quirks.New(""), nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.FileExists(t, squashFile)
|
||||
|
||||
@ -29,21 +29,3 @@ func TarGz(ctx context.Context, rootPath string, output io.Writer, walkerOptions
|
||||
|
||||
return zw.Close()
|
||||
}
|
||||
|
||||
// UntarGz extracts .tar.gz archive to the rootPath.
|
||||
func UntarGz(ctx context.Context, input io.Reader, rootPath string) error {
|
||||
zr, err := gzip.NewReader(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//nolint:errcheck
|
||||
defer zr.Close()
|
||||
|
||||
err = Untar(ctx, zr, rootPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return zr.Close()
|
||||
}
|
||||
|
||||
@ -13,15 +13,19 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/pkg/xattr"
|
||||
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
"github.com/siderolabs/talos/pkg/safepath"
|
||||
)
|
||||
|
||||
// Untar extracts .tar archive from r into filesystem under rootPath.
|
||||
//
|
||||
//nolint:gocyclo
|
||||
func Untar(ctx context.Context, r io.Reader, rootPath string) error {
|
||||
// If xattrsMap is not nil, it will be filled with paths and their corresponding
|
||||
// SELinux xattr values instead of setting the xattrs on the filesystem.
|
||||
//
|
||||
// If xattrsMap is nil, the function will ignore SELinux xattr values.
|
||||
//
|
||||
//nolint:gocyclo,cyclop
|
||||
func Untar(ctx context.Context, r io.Reader, rootPath string, xattrsMap map[string]string) error {
|
||||
tr := tar.NewReader(r)
|
||||
|
||||
for {
|
||||
@ -87,10 +91,8 @@ func Untar(ctx context.Context, r io.Reader, rootPath string) error {
|
||||
}
|
||||
}
|
||||
|
||||
if hdr.PAXRecords["SCHILY.xattr.security.selinux"] != "" {
|
||||
if err = xattr.LSet(path, "security.selinux", []byte(hdr.PAXRecords["SCHILY.xattr.security.selinux"])); err != nil {
|
||||
return fmt.Errorf("error setting selinux xattr for %q: %w", path, err)
|
||||
}
|
||||
if hdr.PAXRecords[constants.TarPaxHeaderSELinux] != "" && xattrsMap != nil {
|
||||
xattrsMap[path] = hdr.PAXRecords[constants.TarPaxHeaderSELinux]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -112,10 +112,10 @@ func BuildEmbeddedConfigExtension(machineConfig []byte) (io.Reader, error) {
|
||||
if err = tw.WriteHeader(&tar.Header{
|
||||
Name: filepath.Join("rootfs", constants.EmbeddedConfigDirectory, constants.ConfigFilename),
|
||||
Typeflag: tar.TypeReg,
|
||||
Mode: 0o000,
|
||||
Mode: 0o400,
|
||||
Size: int64(len(machineConfig)),
|
||||
PAXRecords: map[string]string{
|
||||
"SCHILY.xattr.security.selinux": constants.StateSelinuxLabel,
|
||||
constants.TarPaxHeaderSELinux: constants.StateSelinuxLabel,
|
||||
},
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("failed to write embedded header: %w", err)
|
||||
|
||||
@ -31,6 +31,8 @@ type Builder struct {
|
||||
Printf func(format string, v ...any)
|
||||
// Quirks for the Talos version being used.
|
||||
Quirks quirks.Quirks
|
||||
// XAttrsMap is used to store paths and their corresponding SELinux xattr values during extraction of extensions.
|
||||
XAttrsMap map[string]string
|
||||
}
|
||||
|
||||
// Build rebuilds the initramfs.xz with extensions.
|
||||
@ -122,7 +124,7 @@ func (builder *Builder) compressExtensions(ctx context.Context, extensions []*ex
|
||||
builder.Printf("compressing system extensions")
|
||||
|
||||
for _, ext := range extensions {
|
||||
path, err := ext.Compress(ctx, tempDir, tempDir, builder.Quirks)
|
||||
path, err := ext.Compress(ctx, tempDir, tempDir, builder.Quirks, builder.XAttrsMap)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error compressing extension %q: %w", ext.Manifest.Metadata.Name, err)
|
||||
}
|
||||
|
||||
@ -47,12 +47,16 @@ type Imager struct {
|
||||
|
||||
sdBootPath string
|
||||
ukiPath string
|
||||
|
||||
// xattrsMap is used to store paths and their corresponding SELinux xattr values during extraction of extensions.
|
||||
xattrsMap map[string]string
|
||||
}
|
||||
|
||||
// New creates a new Imager.
|
||||
func New(prof profile.Profile) (*Imager, error) {
|
||||
return &Imager{
|
||||
prof: prof,
|
||||
prof: prof,
|
||||
xattrsMap: map[string]string{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -198,7 +202,7 @@ func (i *Imager) handleOverlay(ctx context.Context, report *reporter.Reporter) e
|
||||
return fmt.Errorf("failed to create overlay directory: %w", err)
|
||||
}
|
||||
|
||||
if err := i.prof.Overlay.Image.Extract(ctx, tempOverlayPath, runtime.GOARCH, progressPrintf(report, reporter.Update{Message: "pulling overlay...", Status: reporter.StatusRunning})); err != nil {
|
||||
if err := i.prof.Overlay.Image.Extract(ctx, tempOverlayPath, runtime.GOARCH, progressPrintf(report, reporter.Update{Message: "pulling overlay...", Status: reporter.StatusRunning}), nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -316,7 +320,7 @@ func (i *Imager) buildInitramfs(ctx context.Context, report *reporter.Reporter)
|
||||
return fmt.Errorf("failed to create extension directory: %w", err)
|
||||
}
|
||||
|
||||
if err := ext.Extract(ctx, extensionDir, i.prof.Arch, printf); err != nil {
|
||||
if err := ext.Extract(ctx, extensionDir, i.prof.Arch, printf, i.xattrsMap); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -328,6 +332,7 @@ func (i *Imager) buildInitramfs(ctx context.Context, report *reporter.Reporter)
|
||||
ExtensionTreePath: extensionsCheckoutDir,
|
||||
Printf: printf,
|
||||
Quirks: quirks.New(i.prof.Version),
|
||||
XAttrsMap: i.xattrsMap,
|
||||
}
|
||||
|
||||
if err := builder.Build(ctx); err != nil {
|
||||
|
||||
@ -97,7 +97,7 @@ func (i *Imager) outISO(ctx context.Context, path string, report *reporter.Repor
|
||||
return err
|
||||
}
|
||||
|
||||
if err := i.prof.Input.ImageCache.Extract(ctx, filepath.Join(scratchSpace, "imagecache"), i.prof.Arch, printf); err != nil {
|
||||
if err := i.prof.Input.ImageCache.Extract(ctx, filepath.Join(scratchSpace, "imagecache"), i.prof.Arch, printf, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -358,7 +358,7 @@ func (i *Imager) buildImage(ctx context.Context, path string, printf func(string
|
||||
return err
|
||||
}
|
||||
|
||||
if err := i.prof.Input.ImageCache.Extract(ctx, imageCacheDir, i.prof.Arch, printf); err != nil {
|
||||
if err := i.prof.Input.ImageCache.Extract(ctx, imageCacheDir, i.prof.Arch, printf, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -540,6 +540,7 @@ func (i *Imager) outInstaller(ctx context.Context, path string, report *reporter
|
||||
tempOverlayPath,
|
||||
i.prof.Arch,
|
||||
progressPrintf(report, reporter.Update{Message: "pulling overlay for installer...", Status: reporter.StatusRunning}),
|
||||
nil,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -320,7 +320,12 @@ func (c *ContainerAsset) pullFromOCI(arch string) (v1.Image, error) {
|
||||
}
|
||||
|
||||
// Extract the container asset to the path.
|
||||
func (c *ContainerAsset) Extract(ctx context.Context, destination, arch string, printf func(string, ...any)) error {
|
||||
func (c *ContainerAsset) Extract(
|
||||
ctx context.Context,
|
||||
destination, arch string,
|
||||
printf func(string, ...any),
|
||||
xattrsMap map[string]string,
|
||||
) error {
|
||||
if c.TarballPath != "" {
|
||||
in, err := os.Open(c.TarballPath)
|
||||
if err != nil {
|
||||
@ -331,7 +336,7 @@ func (c *ContainerAsset) Extract(ctx context.Context, destination, arch string,
|
||||
|
||||
printf("extracting %s...", c.TarballPath)
|
||||
|
||||
return archiver.Untar(ctx, in, destination)
|
||||
return archiver.Untar(ctx, in, destination, xattrsMap)
|
||||
}
|
||||
|
||||
img, err := c.Pull(ctx, arch, printf)
|
||||
@ -356,7 +361,7 @@ func (c *ContainerAsset) Extract(ctx context.Context, destination, arch string,
|
||||
})
|
||||
|
||||
eg.Go(func() error {
|
||||
if untarErr := archiver.Untar(ctx, r, destination); untarErr != nil {
|
||||
if untarErr := archiver.Untar(ctx, r, destination, xattrsMap); untarErr != nil {
|
||||
r.CloseWithError(untarErr)
|
||||
|
||||
return untarErr
|
||||
|
||||
@ -1329,6 +1329,9 @@ const (
|
||||
|
||||
// ImageLabelVerified is the label key for the verified image label.
|
||||
ImageLabelVerified = "talos.dev/verified"
|
||||
|
||||
// TarPaxHeaderSELinux is the name of the PAX header for storing SELinux labels.
|
||||
TarPaxHeaderSELinux = "SCHILY.xattr.security.selinux"
|
||||
)
|
||||
|
||||
// names of variable that can be substituted in the talos.config kernel parameter.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user