vendor containers projects to tagged versions

Updates:
 - c/storage v1.56.0
 - c/image v5.33.0
 - c/common v0.61.0
 - c/buildah v1.38.0
 - c/libhvee v0.9.0
 - github.com/crc-org/crc/v2 v2.43.0

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
This commit is contained in:
Paul Holzinger
2024-11-12 10:51:22 +01:00
parent 7a3e8da8ea
commit 42e8322532
14 changed files with 531 additions and 136 deletions

View File

@ -32,11 +32,12 @@ var (
// ipLocalhost is a regex pattern for IPv4 or IPv6 loopback range.
ipLocalhost = `((127\.([0-9]{1,3}\.){2}[0-9]{1,3})|(::1)$)`
localhostNSRegexp = regexp.Delayed(`(?m)^nameserver\s+` + ipLocalhost + `\s*\n*`)
nsIPv6Regexp = regexp.Delayed(`(?m)^nameserver\s+` + ipv6Address + `\s*\n*`)
nsRegexp = regexp.Delayed(`^\s*nameserver\s*((` + ipv4Address + `)|(` + ipv6Address + `))\s*$`)
searchRegexp = regexp.Delayed(`^\s*search\s*(([^\s]+\s*)*)$`)
optionsRegexp = regexp.Delayed(`^\s*options\s*(([^\s]+\s*)*)$`)
localhostNSRegexp = regexp.Delayed(`(?m)^nameserver\s+` + ipLocalhost + `\s*\n*`)
nsIPv6Regexp = regexp.Delayed(`(?m)^nameserver\s+` + ipv6Address + `\s*\n*`)
nsIPv6LinkLocalRegexp = regexp.Delayed(`(?m)^nameserver\s+` + ipv6Address + `%.*\s*\n*`)
nsRegexp = regexp.Delayed(`^\s*nameserver\s*((` + ipv4Address + `)|(` + ipv6Address + `))\s*$`)
searchRegexp = regexp.Delayed(`^\s*search\s*(([^\s]+\s*)*)$`)
optionsRegexp = regexp.Delayed(`^\s*options\s*(([^\s]+\s*)*)$`)
)
// filterResolvDNS cleans up the config in resolvConf. It has two main jobs:
@ -54,6 +55,10 @@ func filterResolvDNS(resolvConf []byte, ipv6Enabled bool, netnsEnabled bool) []b
// if IPv6 is not enabled, also clean out any IPv6 address nameserver
if !ipv6Enabled {
cleanedResolvConf = nsIPv6Regexp.ReplaceAll(cleanedResolvConf, []byte{})
} else {
// If ipv6 is we still must remove any ipv6 link-local addresses as
// the zone will never match the interface name or index in the container.
cleanedResolvConf = nsIPv6LinkLocalRegexp.ReplaceAll(cleanedResolvConf, []byte{})
}
// if the resulting resolvConf has no more nameservers defined, add appropriate
// default DNS servers for IPv4 and (optionally) IPv6

View File

@ -4,8 +4,12 @@ package cgroups
import (
"context"
"errors"
"fmt"
"math/big"
"path/filepath"
"slices"
"strconv"
"strings"
systemdDbus "github.com/coreos/go-systemd/v22/dbus"
@ -53,7 +57,11 @@ func systemdCreate(resources *configs.Resources, path string, c *systemdDbus.Con
properties = append(properties, p)
}
uMap, sMap, bMap, iMap, structMap := resourcesToProps(resources, v2)
uMap, sMap, bMap, iMap, structMap, err := resourcesToProps(resources, v2)
if err != nil {
lastError = err
continue
}
for k, v := range uMap {
p := systemdDbus.Property{
Name: k,
@ -95,7 +103,7 @@ func systemdCreate(resources *configs.Resources, path string, c *systemdDbus.Con
}
ch := make(chan string)
_, err := c.StartTransientUnitContext(context.TODO(), name, "replace", properties, ch)
_, err = c.StartTransientUnitContext(context.TODO(), name, "replace", properties, ch)
if err != nil {
lastError = err
continue
@ -142,7 +150,7 @@ func systemdDestroyConn(path string, c *systemdDbus.Conn) error {
return nil
}
func resourcesToProps(res *configs.Resources, v2 bool) (map[string]uint64, map[string]string, map[string][]byte, map[string]int64, map[string][]BlkioDev) {
func resourcesToProps(res *configs.Resources, v2 bool) (map[string]uint64, map[string]string, map[string][]byte, map[string]int64, map[string][]BlkioDev, error) {
bMap := make(map[string][]byte)
// this array is not used but will be once more resource limits are added
sMap := make(map[string]string)
@ -179,11 +187,19 @@ func resourcesToProps(res *configs.Resources, v2 bool) (map[string]uint64, map[s
// CPUSet
if res.CpusetCpus != "" {
bits := []byte(res.CpusetCpus)
bits, err := rangeToBits(res.CpusetCpus)
if err != nil {
return nil, nil, nil, nil, nil, fmt.Errorf("resources.CpusetCpus=%q conversion error: %w",
res.CpusetCpus, err)
}
bMap["AllowedCPUs"] = bits
}
if res.CpusetMems != "" {
bits := []byte(res.CpusetMems)
bits, err := rangeToBits(res.CpusetMems)
if err != nil {
return nil, nil, nil, nil, nil, fmt.Errorf("resources.CpusetMems=%q conversion error: %w",
res.CpusetMems, err)
}
bMap["AllowedMemoryNodes"] = bits
}
@ -258,5 +274,51 @@ func resourcesToProps(res *configs.Resources, v2 bool) (map[string]uint64, map[s
}
}
return uMap, sMap, bMap, iMap, structMap
return uMap, sMap, bMap, iMap, structMap, nil
}
func rangeToBits(str string) ([]byte, error) {
bits := new(big.Int)
for _, r := range strings.Split(str, ",") {
// allow extra spaces around
r = strings.TrimSpace(r)
// allow empty elements (extra commas)
if r == "" {
continue
}
startr, endr, ok := strings.Cut(r, "-")
if ok {
start, err := strconv.ParseUint(startr, 10, 32)
if err != nil {
return nil, err
}
end, err := strconv.ParseUint(endr, 10, 32)
if err != nil {
return nil, err
}
if start > end {
return nil, errors.New("invalid range: " + r)
}
for i := start; i <= end; i++ {
bits.SetBit(bits, int(i), 1)
}
} else {
val, err := strconv.ParseUint(startr, 10, 32)
if err != nil {
return nil, err
}
bits.SetBit(bits, int(val), 1)
}
}
ret := bits.Bytes()
if len(ret) == 0 {
// do not allow empty values
return nil, errors.New("empty value")
}
// fit cpuset parsing order in systemd
slices.Reverse(ret)
return ret, nil
}

View File

@ -260,34 +260,37 @@ func newNSPath(nsPath string) (ns.NetNS, error) {
// UnmountNS unmounts the given netns path
func UnmountNS(nsPath string) error {
// Only unmount if it's been bind-mounted (don't touch namespaces in /proc...)
if !strings.HasPrefix(nsPath, "/proc/") {
// EINVAL means the path exists but is not mounted, just try to remove the path below
if err := unix.Unmount(nsPath, unix.MNT_DETACH); err != nil && !errors.Is(err, unix.EINVAL) {
// If path does not exists we can return without error as we have nothing to do.
if strings.HasPrefix(nsPath, "/proc/") {
return nil
}
// EINVAL means the path exists but is not mounted, just try to remove the path below
if err := unix.Unmount(nsPath, unix.MNT_DETACH); err != nil && !errors.Is(err, unix.EINVAL) {
// If path does not exists we can return without error as we have nothing to do.
if errors.Is(err, unix.ENOENT) {
return nil
}
return fmt.Errorf("failed to unmount NS: at %s: %w", nsPath, err)
}
var err error
// wait for up to 60s in the loop
for range 6000 {
if err = os.Remove(nsPath); err != nil {
if errors.Is(err, unix.EBUSY) {
// mount is still busy, sleep a moment and try again to remove
logrus.Debugf("Netns %s still busy, try removing it again in 10ms", nsPath)
time.Sleep(10 * time.Millisecond)
continue
}
// If path does not exists we can return without error.
if errors.Is(err, unix.ENOENT) {
return nil
}
return fmt.Errorf("failed to unmount NS: at %s: %w", nsPath, err)
}
for {
if err := os.Remove(nsPath); err != nil {
if errors.Is(err, unix.EBUSY) {
// mount is still busy, sleep a moment and try again to remove
logrus.Debugf("Netns %s still busy, try removing it again in 10ms", nsPath)
time.Sleep(10 * time.Millisecond)
continue
}
// If path does not exists we can return without error.
if errors.Is(err, unix.ENOENT) {
break
}
return fmt.Errorf("failed to remove ns path: %w", err)
}
break
return fmt.Errorf("failed to remove ns path: %w", err)
}
return nil
}
return nil
return fmt.Errorf("failed to remove ns path (timeout after 60s): %w", err)
}

View File

@ -155,6 +155,25 @@ func getMountsMap(path string) (string, string, error) { //nolint
return "", "", fmt.Errorf("unable to get host and container dir from path: %s", path)
}
// Return true iff the system is in FIPS mode as determined by reading
// /proc/sys/crypto/fips_enabled.
func shouldAddFIPSMounts() bool {
fips_enabled, err := os.ReadFile("/proc/sys/crypto/fips_enabled")
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
logrus.Errorf("Failed to read /proc/sys/crypto/fips_enabled to determine FIPS state: %v", err)
}
return false
}
if strings.TrimSpace(string(fips_enabled)) != "1" {
logrus.Debug("/proc/sys/crypto/fips_enabled does not contain '1', not adding FIPS mode bind mounts")
return false
}
return true
}
// MountsWithUIDGID copies, adds, and mounts the subscriptions to the container root filesystem
// mountLabel: MAC/SELinux label for container content
// containerRunDir: Private data for storing subscriptions on the host mounted in container.
@ -194,22 +213,16 @@ func MountsWithUIDGID(mountLabel, containerRunDir, mountFile, mountPoint string,
}
}
// Only add FIPS subscription mount if disableFips=false
if disableFips {
// Only add FIPS subscription mount if disableFips is false and
// /proc/sys/crypto/fips_enabled contains "1"
if disableFips || !shouldAddFIPSMounts() {
return subscriptionMounts
}
// Add FIPS mode subscription if /etc/system-fips exists on the host
err := fileutils.Exists("/etc/system-fips")
switch {
case err == nil:
if err := addFIPSModeSubscription(&subscriptionMounts, containerRunDir, mountPoint, mountLabel, uid, gid); err != nil {
logrus.Errorf("Adding FIPS mode subscription to container: %v", err)
}
case errors.Is(err, os.ErrNotExist):
logrus.Debug("/etc/system-fips does not exist on host, not mounting FIPS mode subscription")
default:
logrus.Errorf("stat /etc/system-fips failed for FIPS mode subscription: %v", err)
if err := addFIPSMounts(&subscriptionMounts, containerRunDir, mountPoint, mountLabel, uid, gid); err != nil {
logrus.Errorf("Adding FIPS mode bind mounts to container: %v", err)
}
return subscriptionMounts
}
@ -306,43 +319,97 @@ func addSubscriptionsFromMountsFile(filePath, mountLabel, containerRunDir string
return mounts, nil
}
// addFIPSModeSubscription adds mounts to the `mounts` slice that are needed for the container to run openssl in FIPs mode
// (i.e: be FIPs compliant).
// It should only be called if /etc/system-fips exists on host.
// It primarily does two things:
// - creates /run/secrets/system-fips in the container root filesystem, and adds it to the `mounts` slice.
// - If `/etc/crypto-policies/back-ends` already exists inside of the container, it creates
// `/usr/share/crypto-policies/back-ends/FIPS` inside the container as well.
// It is done from within the container to ensure to avoid policy incompatibility between the container and host.
func addFIPSModeSubscription(mounts *[]rspec.Mount, containerRunDir, mountPoint, mountLabel string, uid, gid int) error {
subscriptionsDir := "/run/secrets"
ctrDirOnHost := filepath.Join(containerRunDir, subscriptionsDir)
if err := fileutils.Exists(ctrDirOnHost); errors.Is(err, os.ErrNotExist) {
if err = idtools.MkdirAllAs(ctrDirOnHost, 0o755, uid, gid); err != nil { //nolint
return err
}
if err = label.Relabel(ctrDirOnHost, mountLabel, false); err != nil {
return fmt.Errorf("applying correct labels on %q: %w", ctrDirOnHost, err)
}
func containerHasEtcSystemFips(subscriptionsDir, mountPoint string) (bool, error) {
containerEtc, err := securejoin.SecureJoin(mountPoint, "etc")
if err != nil {
return false, fmt.Errorf("Container /etc resolution error: %w", err)
}
fipsFile := filepath.Join(ctrDirOnHost, "system-fips")
// In the event of restart, it is possible for the FIPS mode file to already exist
if err := fileutils.Exists(fipsFile); errors.Is(err, os.ErrNotExist) {
file, err := os.Create(fipsFile)
if err != nil {
return fmt.Errorf("creating system-fips file in container for FIPS mode: %w", err)
}
file.Close()
if fileutils.Lexists(filepath.Join(containerEtc, "system-fips")) != nil {
logrus.Debug("/etc/system-fips does not exist in the container, not creating /run/secrets/system-fips")
return false, nil
}
if !mountExists(*mounts, subscriptionsDir) {
m := rspec.Mount{
Source: ctrDirOnHost,
Destination: subscriptionsDir,
Type: "bind",
Options: []string{"bind", "rprivate"},
fipsFileTarget, err := securejoin.SecureJoin(mountPoint, "etc/system-fips")
if err != nil {
return false, fmt.Errorf("Container /etc/system-fips resolution error: %w", err)
}
if fipsFileTarget != filepath.Join(mountPoint, subscriptionsDir, "system-fips") {
logrus.Warnf("/etc/system-fips exists in the container, but is not a symlink to %[1]v/system-fips; not creating %[1]v/system-fips", subscriptionsDir)
return false, nil
}
return true, nil
}
// addFIPSMounts adds mounts to the `mounts` slice that are needed
// for the container to run cryptographic libraries (openssl, gnutls, NSS, ...)
// in FIPS mode (i.e: be FIPS compliant).
// It should only be called if /proc/sys/crypto/fips_enabled on the host
// contains '1'.
// It does three things:
// - creates /run/secrets/system-fips in the container root filesystem if
// /etc/system-fips exists and is a symlink to /run/secrets/system-fips,
// and adds it to the `mounts` slice. This is, for example, the case on
// RHEL 8, but not on newer RHEL, since /etc/system-fips is deprecated.
// - Bind-mounts `/usr/share/crypto-policies/back-ends/FIPS` over
// `/etc/crypto-policies/back-ends` if the former exists inside of the
// container. This is done from within the container to avoid policy
// incompatibility between container and host.
// - If a bind mount for `/etc/crypto-policies/back-ends` was created,
// bind-mounts `/usr/share/crypto-policies/default-fips-config` over
// `/etc/crypto-policies/config` if the former exists inside of the
// container. If it does not exist, creates a new temporary file containing
// "FIPS\n", and bind-mounts that over `/etc/crypto-policies/config`.
//
// Starting in CentOS 10 Stream, the crypto-policies package gracefully recognizes the two bind mounts
//
// - /etc/crypto-policies/config -> /usr/share/crypto-policies/default-fips-config
// - /etc/crypto-policies/back-ends/FIPS -> /usr/share/crypto-policies/back-ends/FIPS
//
// and unmounts them when users manually change the policy, or removes and
// restores the mounts when the crypto-policies package is upgraded.
func addFIPSMounts(mounts *[]rspec.Mount, containerRunDir, mountPoint, mountLabel string, uid, gid int) error {
// Check whether $container/etc/system-fips exists and is a symlink to /run/secrets/system-fips
subscriptionsDir := "/run/secrets"
createSystemFipsSecret, err := containerHasEtcSystemFips(subscriptionsDir, mountPoint)
if err != nil {
return err
}
if createSystemFipsSecret {
// This container contains
// /etc/system-fips -> /run/secrets/system-fips
// and expects podman to create this file if the container should
// be in FIPS mode
ctrDirOnHost := filepath.Join(containerRunDir, subscriptionsDir)
if err := fileutils.Exists(ctrDirOnHost); errors.Is(err, os.ErrNotExist) {
if err = idtools.MkdirAllAs(ctrDirOnHost, 0o755, uid, gid); err != nil { //nolint
return err
}
if err = label.Relabel(ctrDirOnHost, mountLabel, false); err != nil {
return fmt.Errorf("applying correct labels on %q: %w", ctrDirOnHost, err)
}
}
fipsFile := filepath.Join(ctrDirOnHost, "system-fips")
// In the event of restart, it is possible for the FIPS mode file to already exist
if err := fileutils.Exists(fipsFile); errors.Is(err, os.ErrNotExist) {
file, err := os.Create(fipsFile)
if err != nil {
return fmt.Errorf("creating system-fips file in container for FIPS mode: %w", err)
}
file.Close()
}
if !mountExists(*mounts, subscriptionsDir) {
m := rspec.Mount{
Source: ctrDirOnHost,
Destination: subscriptionsDir,
Type: "bind",
Options: []string{"bind", "rprivate"},
}
*mounts = append(*mounts, m)
}
*mounts = append(*mounts, m)
}
srcBackendDir := "/usr/share/crypto-policies/back-ends/FIPS"
@ -370,27 +437,44 @@ func addFIPSModeSubscription(mounts *[]rspec.Mount, containerRunDir, mountPoint,
// Make sure we set the config to FIPS so that the container does not overwrite
// /etc/crypto-policies/back-ends when crypto-policies-scripts is reinstalled.
cryptoPoliciesConfigFile := filepath.Join(containerRunDir, "fips-config")
file, err := os.Create(cryptoPoliciesConfigFile)
//
// Starting in CentOS 10 Stream, crypto-policies provides
// /usr/share/crypto-policies/default-fips-config as bind mount source
// file and the crypto-policies tooling gracefully deals with the two bind-mounts
// /etc/crypto-policies/back-ends -> /usr/share/crypto-policies/back-ends/FIPS
// /etc/crypto-policies/config -> /usr/share/crypto-policies/default-fips-config
// if they both exist.
srcPolicyConfig := "/usr/share/crypto-policies/default-fips-config"
destPolicyConfig := "/etc/crypto-policies/config"
srcPolicyConfigOnHost, err := securejoin.SecureJoin(mountPoint, srcPolicyConfig)
if err != nil {
return fmt.Errorf("creating fips config file in container for FIPS mode: %w", err)
}
defer file.Close()
if _, err := file.WriteString("FIPS\n"); err != nil {
return fmt.Errorf("writing fips config file in container for FIPS mode: %w", err)
}
if err = label.Relabel(cryptoPoliciesConfigFile, mountLabel, false); err != nil {
return fmt.Errorf("applying correct labels on fips-config file: %w", err)
}
if err := file.Chown(uid, gid); err != nil {
return fmt.Errorf("chown fips-config file: %w", err)
return fmt.Errorf("Could not expand %q in container: %w", srcPolicyConfig, err)
}
policyConfig := "/etc/crypto-policies/config"
if !mountExists(*mounts, policyConfig) {
if err = fileutils.Exists(srcPolicyConfigOnHost); err != nil {
if !errors.Is(err, os.ErrNotExist) {
return fmt.Errorf("Could not check whether %q exists in container: %w", srcPolicyConfig, err)
}
// /usr/share/crypto-policies/default-fips-config does not exist, let's create it ourselves
cryptoPoliciesConfigFile := filepath.Join(containerRunDir, "fips-config")
if err := os.WriteFile(cryptoPoliciesConfigFile, []byte("FIPS\n"), 0o644); err != nil {
return fmt.Errorf("Failed to write fips config file in container for FIPS mode: %w", err)
}
if err = label.Relabel(cryptoPoliciesConfigFile, mountLabel, false); err != nil {
return fmt.Errorf("Failed to apply correct labels on fips config file: %w", err)
}
if err := os.Chown(cryptoPoliciesConfigFile, uid, gid); err != nil {
return fmt.Errorf("Failed to chown fips config file: %w", err)
}
srcPolicyConfigOnHost = cryptoPoliciesConfigFile
}
if !mountExists(*mounts, destPolicyConfig) {
m := rspec.Mount{
Source: cryptoPoliciesConfigFile,
Destination: policyConfig,
Source: srcPolicyConfigOnHost,
Destination: destPolicyConfig,
Type: "bind",
Options: []string{"bind", "rprivate"},
}

View File

@ -1,4 +1,4 @@
package version
// Version is the version of the build.
const Version = "0.61.0-dev"
const Version = "0.61.0"