Andrey Smirnov 58cb9db1e2
feat: allow hardlinks in the system extension images
They should cause no harm as every extension as an image on its own, so
hardlinks are only available between the files in one image only.

Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
2022-03-21 15:38:34 +03:00

102 lines
2.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 extensions
import (
"fmt"
"io/fs"
"os"
"path/filepath"
"strings"
hashiversion "github.com/hashicorp/go-version"
"github.com/talos-systems/talos/pkg/machinery/extensions"
"github.com/talos-systems/talos/pkg/version"
)
// Validate the extension: compatibility, contents, etc.
func (ext *Extension) Validate() error {
if err := ext.validateConstraints(); err != nil {
return err
}
if err := ext.validateContents(); err != nil {
return err
}
return nil
}
func (ext *Extension) validateConstraints() error {
if ext.Manifest.Metadata.Compatibility.Talos.Version != "" {
talosVersion, err := hashiversion.NewVersion(version.Tag)
if err != nil {
return err
}
versionConstraint, err := hashiversion.NewConstraint(ext.Manifest.Metadata.Compatibility.Talos.Version)
if err != nil {
return fmt.Errorf("error parsing Talos version constraint: %w", err)
}
if !versionConstraint.Check(talosVersion.Core()) {
return fmt.Errorf("version constraint %s can't be satisfied with Talos version %s", versionConstraint, talosVersion)
}
}
return nil
}
//nolint:gocyclo
func (ext *Extension) validateContents() error {
return filepath.WalkDir(ext.rootfsPath, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
itemPath, err := filepath.Rel(ext.rootfsPath, path)
if err != nil {
return err
}
itemPath = filepath.Join("/", itemPath)
// check for -------w-
if d.Type().Perm()&0o002 > 0 {
return fmt.Errorf("world-writeable files are not allowed: %q", itemPath)
}
// no special files
if !d.IsDir() && !d.Type().IsRegular() && d.Type().Type() != os.ModeSymlink {
return fmt.Errorf("special files are not allowed: %q", itemPath)
}
// regular file: check for file path being whitelisted
if !d.IsDir() {
dirPath := filepath.Dir(itemPath)
allowed := false
for _, allowedPath := range extensions.AllowedPaths {
if strings.HasPrefix(dirPath, allowedPath) {
_, err = filepath.Rel(allowedPath, dirPath)
if err == nil {
allowed = true
break
}
}
}
if !allowed {
return fmt.Errorf("path %q is not allowed in extensions", itemPath)
}
}
return nil
})
}