mirror of
https://github.com/containers/podman.git
synced 2025-10-16 18:53:19 +08:00
Merge pull request #26718 from ninja-quokka/machine_init_tls_verify
Add support for configuring tls verification with machine init
This commit is contained in:
@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"github.com/containers/common/pkg/completion"
|
"github.com/containers/common/pkg/completion"
|
||||||
"github.com/containers/common/pkg/strongunits"
|
"github.com/containers/common/pkg/strongunits"
|
||||||
|
"github.com/containers/image/v5/types"
|
||||||
"github.com/containers/podman/v5/cmd/podman/registry"
|
"github.com/containers/podman/v5/cmd/podman/registry"
|
||||||
ldefine "github.com/containers/podman/v5/libpod/define"
|
ldefine "github.com/containers/podman/v5/libpod/define"
|
||||||
"github.com/containers/podman/v5/libpod/events"
|
"github.com/containers/podman/v5/libpod/events"
|
||||||
@ -41,6 +42,7 @@ var (
|
|||||||
// Flags which have a meaning when unspecified that differs from the flag default
|
// Flags which have a meaning when unspecified that differs from the flag default
|
||||||
type InitOptionalFlags struct {
|
type InitOptionalFlags struct {
|
||||||
UserModeNetworking bool
|
UserModeNetworking bool
|
||||||
|
tlsVerify bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// maxMachineNameSize is set to thirty to limit huge machine names primarily
|
// maxMachineNameSize is set to thirty to limit huge machine names primarily
|
||||||
@ -154,6 +156,9 @@ func init() {
|
|||||||
userModeNetFlagName := "user-mode-networking"
|
userModeNetFlagName := "user-mode-networking"
|
||||||
flags.BoolVar(&initOptionalFlags.UserModeNetworking, userModeNetFlagName, false,
|
flags.BoolVar(&initOptionalFlags.UserModeNetworking, userModeNetFlagName, false,
|
||||||
"Whether this machine should use user-mode networking, routing traffic through a host user-space process")
|
"Whether this machine should use user-mode networking, routing traffic through a host user-space process")
|
||||||
|
|
||||||
|
flags.BoolVar(&initOptionalFlags.tlsVerify, "tls-verify", true,
|
||||||
|
"Require HTTPS and verify certificates when contacting registries")
|
||||||
}
|
}
|
||||||
|
|
||||||
func initMachine(cmd *cobra.Command, args []string) error {
|
func initMachine(cmd *cobra.Command, args []string) error {
|
||||||
@ -219,6 +224,16 @@ func initMachine(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// initOpts.SkipTlsVerify defaults to OptionalBoolUndefined, which means the backend library
|
||||||
|
// decides whether to verify TLS. We only explicitly set it if the user specifies the
|
||||||
|
// --tls-verify flag on the CLI.
|
||||||
|
//
|
||||||
|
// The flag value from initOptionalFlags.tlsVerify indicates whether TLS verification is desired.
|
||||||
|
// Since we are converting tlsVerify -> SkipTlsVerify, we must invert the bool accordingly.
|
||||||
|
if cmd.Flags().Changed("tls-verify") {
|
||||||
|
initOpts.SkipTlsVerify = types.NewOptionalBool(!initOptionalFlags.tlsVerify)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO need to work this back in
|
// TODO need to work this back in
|
||||||
// if finished, err := vm.Init(initOpts); err != nil || !finished {
|
// if finished, err := vm.Init(initOpts); err != nil || !finished {
|
||||||
// // Finished = true, err = nil - Success! Log a message with further instructions
|
// // Finished = true, err = nil - Success! Log a message with further instructions
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
####> This option file is used in:
|
####> This option file is used in:
|
||||||
####> podman artifact pull, artifact push, auto update, build, container runlabel, create, farm build, kube play, login, manifest add, manifest create, manifest inspect, manifest push, pull, push, run, search
|
####> podman artifact pull, artifact push, auto update, build, container runlabel, create, farm build, kube play, login, machine init, manifest add, manifest create, manifest inspect, manifest push, pull, push, run, search
|
||||||
####> If file is edited, make sure the changes
|
####> If file is edited, make sure the changes
|
||||||
####> are applicable to all of those.
|
####> are applicable to all of those.
|
||||||
#### **--tls-verify**
|
#### **--tls-verify**
|
||||||
|
@ -119,6 +119,8 @@ means to use the timezone of the machine host.
|
|||||||
The timezone setting is not used with WSL. WSL automatically sets the timezone to the same
|
The timezone setting is not used with WSL. WSL automatically sets the timezone to the same
|
||||||
as the host Windows operating system.
|
as the host Windows operating system.
|
||||||
|
|
||||||
|
@@option tls-verify
|
||||||
|
|
||||||
#### **--usb**=*bus=number,devnum=number* or *vendor=hexadecimal,product=hexadecimal*
|
#### **--usb**=*bus=number,devnum=number* or *vendor=hexadecimal,product=hexadecimal*
|
||||||
|
|
||||||
Assign a USB device from the host to the VM via USB passthrough.
|
Assign a USB device from the host to the VM via USB passthrough.
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
package define
|
package define
|
||||||
|
|
||||||
import "net/url"
|
import (
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/containers/image/v5/types"
|
||||||
|
)
|
||||||
|
|
||||||
type InitOptions struct {
|
type InitOptions struct {
|
||||||
PlaybookPath string
|
PlaybookPath string
|
||||||
@ -21,4 +25,5 @@ type InitOptions struct {
|
|||||||
UID string // uid of the user that called machine
|
UID string // uid of the user that called machine
|
||||||
UserModeNetworking *bool // nil = use backend/system default, false = disable, true = enable
|
UserModeNetworking *bool // nil = use backend/system default, false = disable, true = enable
|
||||||
USBs []string
|
USBs []string
|
||||||
|
SkipTlsVerify types.OptionalBool
|
||||||
}
|
}
|
||||||
|
@ -288,18 +288,39 @@ var _ = Describe("run basic podman commands", func() {
|
|||||||
|
|
||||||
It("CVE-2025-6032 regression test - HTTP", func() {
|
It("CVE-2025-6032 regression test - HTTP", func() {
|
||||||
// ensure that trying to pull from a local HTTP server fails and the connection will be rejected
|
// ensure that trying to pull from a local HTTP server fails and the connection will be rejected
|
||||||
testImagePullTLS(nil)
|
// ensure that tlsVerify is true by default
|
||||||
|
testImagePullTLS(nil, nil)
|
||||||
})
|
})
|
||||||
|
|
||||||
It("CVE-2025-6032 regression test - HTTPS unknown cert", func() {
|
It("CVE-2025-6032 regression test - HTTPS unknown cert", func() {
|
||||||
// ensure that trying to pull from an local HTTPS server with invalid certs fails and the connection will be rejected
|
// ensure that trying to pull from a local HTTPS server with invalid certs fails and the connection will be rejected
|
||||||
|
// ensure that tlsVerify is true by default
|
||||||
testImagePullTLS(&TLSConfig{
|
testImagePullTLS(&TLSConfig{
|
||||||
// Key/Cert was generated with:
|
// Key/Cert was generated with:
|
||||||
// openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:secp384r1 -days 3650 \
|
// openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:secp384r1 -days 3650 \
|
||||||
// -nodes -keyout test-tls.key -out test-tls.crt -subj "/CN=test.podman.io" -addext "subjectAltName=IP:127.0.0.1"
|
// -nodes -keyout test-tls.key -out test-tls.crt -subj "/CN=test.podman.io" -addext "subjectAltName=IP:127.0.0.1"
|
||||||
key: "test-tls.key",
|
key: "test-tls.key",
|
||||||
cert: "test-tls.crt",
|
cert: "test-tls.crt",
|
||||||
})
|
}, nil)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("machine init should not fail on TLS validation with --tls-verfy=false - HTTP", func() {
|
||||||
|
// ensure that trying to pull from a local HTTP server doesn't fail when --tls-verify=false is set
|
||||||
|
tlsVerify := false
|
||||||
|
testImagePullTLS(nil, &tlsVerify)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("machine init should not fail on TLS validation with --tls-verfy=false - HTTPS", func() {
|
||||||
|
// ensure that trying to pull from a local HTTPS server with invalid certs
|
||||||
|
// doesn't fail due to tls validation when --tls-verify=false is set
|
||||||
|
tlsVerify := false
|
||||||
|
testImagePullTLS(&TLSConfig{
|
||||||
|
// Key/Cert was generated with:
|
||||||
|
// openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:secp384r1 -days 3650 \
|
||||||
|
// -nodes -keyout test-tls.key -out test-tls.crt -subj "/CN=test.podman.io" -addext "subjectAltName=IP:127.0.0.1"
|
||||||
|
key: "test-tls.key",
|
||||||
|
cert: "test-tls.crt",
|
||||||
|
}, &tlsVerify)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -339,7 +360,7 @@ type TLSConfig struct {
|
|||||||
|
|
||||||
// setup a local webserver in the test and then point podman machine init to it
|
// setup a local webserver in the test and then point podman machine init to it
|
||||||
// to verify the connection details.
|
// to verify the connection details.
|
||||||
func testImagePullTLS(tls *TLSConfig) {
|
func testImagePullTLS(tls *TLSConfig, tlsVerify *bool) {
|
||||||
listener, err := net.Listen("tcp4", "127.0.0.1:0")
|
listener, err := net.Listen("tcp4", "127.0.0.1:0")
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
serverAddr := listener.Addr().String()
|
serverAddr := listener.Addr().String()
|
||||||
@ -367,9 +388,17 @@ func testImagePullTLS(tls *TLSConfig) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
name := randomString()
|
|
||||||
i := new(initMachine)
|
i := new(initMachine)
|
||||||
session, err := mb.setName(name).setCmd(i.withImage("docker://" + serverAddr + "/testimage")).run()
|
|
||||||
|
i.withImage("docker://" + serverAddr + "/testimage")
|
||||||
|
|
||||||
|
if tlsVerify != nil {
|
||||||
|
i.withTlsVerify(tlsVerify)
|
||||||
|
}
|
||||||
|
|
||||||
|
name := randomString()
|
||||||
|
session, err := mb.setName(name).setCmd(i).run()
|
||||||
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(session).To(Exit(125))
|
Expect(session).To(Exit(125))
|
||||||
|
|
||||||
@ -377,7 +406,11 @@ func testImagePullTLS(tls *TLSConfig) {
|
|||||||
// Error: wrong manifest type for disk artifact: text/plain
|
// Error: wrong manifest type for disk artifact: text/plain
|
||||||
// As such we match the errors strings exactly to ensure we have proper error messages that indicate the TLS error.
|
// As such we match the errors strings exactly to ensure we have proper error messages that indicate the TLS error.
|
||||||
expectedErr := "Error: pinging container registry " + serverAddr + ": Get \"https://" + serverAddr + "/v2/\": "
|
expectedErr := "Error: pinging container registry " + serverAddr + ": Get \"https://" + serverAddr + "/v2/\": "
|
||||||
if tls != nil {
|
|
||||||
|
switch {
|
||||||
|
case tlsVerify != nil && *tlsVerify == false: // tls-verify explicitly disabled
|
||||||
|
expectedErr = "Error: wrong manifest type for disk artifact: text/plain\n"
|
||||||
|
case tls != nil:
|
||||||
expectedErr += "tls: failed to verify certificate: x509: "
|
expectedErr += "tls: failed to verify certificate: x509: "
|
||||||
if runtime.GOOS == "darwin" {
|
if runtime.GOOS == "darwin" {
|
||||||
// Apple doesn't like such long valid certs so the error is different but the purpose
|
// Apple doesn't like such long valid certs so the error is different but the purpose
|
||||||
@ -387,13 +420,18 @@ func testImagePullTLS(tls *TLSConfig) {
|
|||||||
} else {
|
} else {
|
||||||
expectedErr += "certificate signed by unknown authority\n"
|
expectedErr += "certificate signed by unknown authority\n"
|
||||||
}
|
}
|
||||||
} else {
|
default:
|
||||||
|
// With both tlsVerify and tls being nil, a HTTP server will be ran and machine init should
|
||||||
|
// default to using tlsVerify
|
||||||
expectedErr += "http: server gave HTTP response to HTTPS client\n"
|
expectedErr += "http: server gave HTTP response to HTTPS client\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
Expect(session.errorToString()).To(Equal(expectedErr))
|
Expect(session.errorToString()).To(Equal(expectedErr))
|
||||||
|
|
||||||
// if the client enforces TLS verification then we should not have received any request
|
// if the client enforces TLS verification then we should not have received any request
|
||||||
Expect(loggedRequests).To(BeEmpty(), "the server should have not process any request from the client")
|
if tlsVerify == nil || *tlsVerify == true {
|
||||||
|
Expect(loggedRequests).To(BeEmpty(), "the server should have not process any request from the client")
|
||||||
|
}
|
||||||
|
|
||||||
srv.Close()
|
srv.Close()
|
||||||
Expect(<-serverErr).To(Equal(http.ErrServerClosed))
|
Expect(<-serverErr).To(Equal(http.ErrServerClosed))
|
||||||
|
@ -11,19 +11,19 @@ import (
|
|||||||
|
|
||||||
type initMachine struct {
|
type initMachine struct {
|
||||||
/*
|
/*
|
||||||
--cpus uint Number of CPUs (default 1)
|
--cpus uint Number of CPUs (default 1)
|
||||||
--disk-size uint Disk size in GiB (default 100)
|
--disk-size uint Disk size in GiB (default 100)
|
||||||
--ignition-path string Path to ignition file
|
--ignition-path string Path to ignition file
|
||||||
--username string Username of the remote user (default "core" for FCOS, "user" for Fedora)
|
--username string Username of the remote user (default "core" for FCOS, "user" for Fedora)
|
||||||
--image-path string Path to bootable image (default "testing")
|
--image-path string Path to bootable image (default "testing")
|
||||||
-m, --memory uint Memory in MiB (default 2048)
|
-m, --memory uint Memory in MiB (default 2048)
|
||||||
--now Start machine now
|
--now Start machine now
|
||||||
--rootful Whether this machine should prefer rootful container execution
|
--rootful Whether this machine should prefer rootful container execution
|
||||||
--playbook string Run an ansible playbook after first boot
|
--playbook string Run an ansible playbook after first boot
|
||||||
--timezone string Set timezone (default "local")
|
--tls-verify Require HTTPS and verify certificates when contacting registries
|
||||||
-v, --volume stringArray Volumes to mount, source:target
|
--timezone string Set timezone (default "local")
|
||||||
--volume-driver string Optional volume driver
|
-v, --volume stringArray Volumes to mount, source:target
|
||||||
|
--volume-driver string Optional volume driver
|
||||||
*/
|
*/
|
||||||
playbook string
|
playbook string
|
||||||
cpus *uint
|
cpus *uint
|
||||||
@ -38,6 +38,7 @@ type initMachine struct {
|
|||||||
rootful bool
|
rootful bool
|
||||||
volumes []string
|
volumes []string
|
||||||
userModeNetworking bool
|
userModeNetworking bool
|
||||||
|
tlsVerify *bool
|
||||||
|
|
||||||
cmd []string
|
cmd []string
|
||||||
}
|
}
|
||||||
@ -85,6 +86,9 @@ func (i *initMachine) buildCmd(m *machineTestBuilder) []string {
|
|||||||
if i.swap != nil {
|
if i.swap != nil {
|
||||||
cmd = append(cmd, "--swap", strconv.Itoa(int(*i.swap)))
|
cmd = append(cmd, "--swap", strconv.Itoa(int(*i.swap)))
|
||||||
}
|
}
|
||||||
|
if i.tlsVerify != nil {
|
||||||
|
cmd = append(cmd, "--tls-verify="+strconv.FormatBool(*i.tlsVerify))
|
||||||
|
}
|
||||||
name := m.name
|
name := m.name
|
||||||
cmd = append(cmd, name)
|
cmd = append(cmd, name)
|
||||||
|
|
||||||
@ -172,6 +176,11 @@ func (i *initMachine) withRunPlaybook(p string) *initMachine {
|
|||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *initMachine) withTlsVerify(tlsVerify *bool) *initMachine {
|
||||||
|
i.tlsVerify = tlsVerify
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
func (i *initMachine) withUserModeNetworking(r bool) *initMachine { //nolint:unused,nolintlint
|
func (i *initMachine) withUserModeNetworking(r bool) *initMachine { //nolint:unused,nolintlint
|
||||||
i.userModeNetworking = r
|
i.userModeNetworking = r
|
||||||
return i
|
return i
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containers/image/v5/types"
|
||||||
"github.com/containers/podman/v5/pkg/machine/compression"
|
"github.com/containers/podman/v5/pkg/machine/compression"
|
||||||
"github.com/containers/podman/v5/pkg/machine/define"
|
"github.com/containers/podman/v5/pkg/machine/define"
|
||||||
"github.com/containers/podman/v5/pkg/machine/ocipull"
|
"github.com/containers/podman/v5/pkg/machine/ocipull"
|
||||||
@ -22,7 +23,9 @@ func pullOCITestDisk(finalDir string, vmType define.VMType) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
dirs := define.MachineDirs{ImageCacheDir: imageCacheDir}
|
dirs := define.MachineDirs{ImageCacheDir: imageCacheDir}
|
||||||
ociArtPull, err := ocipull.NewOCIArtifactPull(context.Background(), &dirs, "", "e2emachine", vmType, unusedFinalPath)
|
|
||||||
|
var skipTlsVerify types.OptionalBool
|
||||||
|
ociArtPull, err := ocipull.NewOCIArtifactPull(context.Background(), &dirs, "", "e2emachine", vmType, unusedFinalPath, skipTlsVerify)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -71,10 +71,8 @@ type DiskArtifactOpts struct {
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func NewOCIArtifactPull(ctx context.Context, dirs *define.MachineDirs, endpoint string, vmName string, vmType define.VMType, finalPath *define.VMFile) (*OCIArtifactDisk, error) {
|
func NewOCIArtifactPull(ctx context.Context, dirs *define.MachineDirs, endpoint string, vmName string, vmType define.VMType, finalPath *define.VMFile, skipTlsVerify types.OptionalBool) (*OCIArtifactDisk, error) {
|
||||||
var (
|
var arch string
|
||||||
arch string
|
|
||||||
)
|
|
||||||
|
|
||||||
artifactVersion := getVersion()
|
artifactVersion := getVersion()
|
||||||
switch runtime.GOARCH {
|
switch runtime.GOARCH {
|
||||||
@ -108,8 +106,10 @@ func NewOCIArtifactPull(ctx context.Context, dirs *define.MachineDirs, endpoint
|
|||||||
imageEndpoint: endpoint,
|
imageEndpoint: endpoint,
|
||||||
machineVersion: artifactVersion,
|
machineVersion: artifactVersion,
|
||||||
name: vmName,
|
name: vmName,
|
||||||
pullOptions: &PullOptions{},
|
pullOptions: &PullOptions{
|
||||||
vmType: vmType,
|
SkipTLSVerify: skipTlsVerify,
|
||||||
|
},
|
||||||
|
vmType: vmType,
|
||||||
}
|
}
|
||||||
return &ociDisk, nil
|
return &ociDisk, nil
|
||||||
}
|
}
|
||||||
|
@ -4,19 +4,20 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containers/image/v5/types"
|
||||||
"github.com/containers/podman/v5/pkg/machine/define"
|
"github.com/containers/podman/v5/pkg/machine/define"
|
||||||
"github.com/containers/podman/v5/pkg/machine/ocipull"
|
"github.com/containers/podman/v5/pkg/machine/ocipull"
|
||||||
"github.com/containers/podman/v5/pkg/machine/stdpull"
|
"github.com/containers/podman/v5/pkg/machine/stdpull"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetDisk(userInputPath string, dirs *define.MachineDirs, imagePath *define.VMFile, vmType define.VMType, name string) error {
|
func GetDisk(userInputPath string, dirs *define.MachineDirs, imagePath *define.VMFile, vmType define.VMType, name string, skipTlsVerify types.OptionalBool) error {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
mydisk ocipull.Disker
|
mydisk ocipull.Disker
|
||||||
)
|
)
|
||||||
|
|
||||||
if userInputPath == "" || strings.HasPrefix(userInputPath, "docker://") {
|
if userInputPath == "" || strings.HasPrefix(userInputPath, "docker://") {
|
||||||
mydisk, err = ocipull.NewOCIArtifactPull(context.Background(), dirs, userInputPath, name, vmType, imagePath)
|
mydisk, err = ocipull.NewOCIArtifactPull(context.Background(), dirs, userInputPath, name, vmType, imagePath, skipTlsVerify)
|
||||||
} else {
|
} else {
|
||||||
if strings.HasPrefix(userInputPath, "http") {
|
if strings.HasPrefix(userInputPath, "http") {
|
||||||
// TODO probably should use tempdir instead of datadir
|
// TODO probably should use tempdir instead of datadir
|
||||||
@ -28,5 +29,6 @@ func GetDisk(userInputPath string, dirs *define.MachineDirs, imagePath *define.V
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return mydisk.Get()
|
return mydisk.Get()
|
||||||
}
|
}
|
||||||
|
@ -172,7 +172,7 @@ func Init(opts machineDefine.InitOptions, mp vmconfigs.VMProvider) error {
|
|||||||
// "/path
|
// "/path
|
||||||
// "docker://quay.io/something/someManifest
|
// "docker://quay.io/something/someManifest
|
||||||
|
|
||||||
if err := diskpull.GetDisk(opts.Image, dirs, mc.ImagePath, mp.VMType(), mc.Name); err != nil {
|
if err := diskpull.GetDisk(opts.Image, dirs, mc.ImagePath, mp.VMType(), mc.Name, opts.SkipTlsVerify); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user