mirror of
https://github.com/containers/podman.git
synced 2025-10-11 16:26:00 +08:00
Add support for configuring tls verification with machine init
This patch adds a new --tls-verify flag to the `podman machine init` sub command which matches many of our other commands. This allows the user to optionally control whether TLS verification is enabled or disabled for download of the machine image. The default remains to leave the TLS verification decision to the backend library which defaults to enabling it, this patch just allows the user to explicitly set it on the CLI. Fixes: #26517 Signed-off-by: Lewis Roy <lewis@redhat.com>
This commit is contained in:
@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/containers/common/pkg/completion"
|
||||
"github.com/containers/common/pkg/strongunits"
|
||||
"github.com/containers/image/v5/types"
|
||||
"github.com/containers/podman/v5/cmd/podman/registry"
|
||||
ldefine "github.com/containers/podman/v5/libpod/define"
|
||||
"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
|
||||
type InitOptionalFlags struct {
|
||||
UserModeNetworking bool
|
||||
tlsVerify bool
|
||||
}
|
||||
|
||||
// maxMachineNameSize is set to thirty to limit huge machine names primarily
|
||||
@ -154,6 +156,9 @@ func init() {
|
||||
userModeNetFlagName := "user-mode-networking"
|
||||
flags.BoolVar(&initOptionalFlags.UserModeNetworking, userModeNetFlagName, false,
|
||||
"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 {
|
||||
@ -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
|
||||
// if finished, err := vm.Init(initOpts); err != nil || !finished {
|
||||
// // Finished = true, err = nil - Success! Log a message with further instructions
|
||||
|
@ -1,5 +1,5 @@
|
||||
####> 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
|
||||
####> are applicable to all of those.
|
||||
#### **--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
|
||||
as the host Windows operating system.
|
||||
|
||||
@@option tls-verify
|
||||
|
||||
#### **--usb**=*bus=number,devnum=number* or *vendor=hexadecimal,product=hexadecimal*
|
||||
|
||||
Assign a USB device from the host to the VM via USB passthrough.
|
||||
|
@ -1,6 +1,10 @@
|
||||
package define
|
||||
|
||||
import "net/url"
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/containers/image/v5/types"
|
||||
)
|
||||
|
||||
type InitOptions struct {
|
||||
PlaybookPath string
|
||||
@ -21,4 +25,5 @@ type InitOptions struct {
|
||||
UID string // uid of the user that called machine
|
||||
UserModeNetworking *bool // nil = use backend/system default, false = disable, true = enable
|
||||
USBs []string
|
||||
SkipTlsVerify types.OptionalBool
|
||||
}
|
||||
|
@ -298,18 +298,39 @@ var _ = Describe("run basic podman commands", 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
|
||||
testImagePullTLS(nil)
|
||||
// ensure that tlsVerify is true by default
|
||||
testImagePullTLS(nil, nil)
|
||||
})
|
||||
|
||||
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{
|
||||
// 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",
|
||||
})
|
||||
}, 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)
|
||||
})
|
||||
})
|
||||
|
||||
@ -349,7 +370,7 @@ type TLSConfig struct {
|
||||
|
||||
// setup a local webserver in the test and then point podman machine init to it
|
||||
// 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")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
serverAddr := listener.Addr().String()
|
||||
@ -377,9 +398,17 @@ func testImagePullTLS(tls *TLSConfig) {
|
||||
}
|
||||
}()
|
||||
|
||||
name := randomString()
|
||||
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(session).To(Exit(125))
|
||||
|
||||
@ -387,7 +416,11 @@ func testImagePullTLS(tls *TLSConfig) {
|
||||
// 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.
|
||||
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: "
|
||||
if runtime.GOOS == "darwin" {
|
||||
// Apple doesn't like such long valid certs so the error is different but the purpose
|
||||
@ -397,13 +430,18 @@ func testImagePullTLS(tls *TLSConfig) {
|
||||
} else {
|
||||
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"
|
||||
}
|
||||
|
||||
Expect(session.errorToString()).To(Equal(expectedErr))
|
||||
|
||||
// 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()
|
||||
Expect(<-serverErr).To(Equal(http.ErrServerClosed))
|
||||
|
@ -11,19 +11,19 @@ import (
|
||||
|
||||
type initMachine struct {
|
||||
/*
|
||||
--cpus uint Number of CPUs (default 1)
|
||||
--disk-size uint Disk size in GiB (default 100)
|
||||
--ignition-path string Path to ignition file
|
||||
--username string Username of the remote user (default "core" for FCOS, "user" for Fedora)
|
||||
--image-path string Path to bootable image (default "testing")
|
||||
-m, --memory uint Memory in MiB (default 2048)
|
||||
--now Start machine now
|
||||
--rootful Whether this machine should prefer rootful container execution
|
||||
--playbook string Run an ansible playbook after first boot
|
||||
--timezone string Set timezone (default "local")
|
||||
-v, --volume stringArray Volumes to mount, source:target
|
||||
--volume-driver string Optional volume driver
|
||||
|
||||
--cpus uint Number of CPUs (default 1)
|
||||
--disk-size uint Disk size in GiB (default 100)
|
||||
--ignition-path string Path to ignition file
|
||||
--username string Username of the remote user (default "core" for FCOS, "user" for Fedora)
|
||||
--image-path string Path to bootable image (default "testing")
|
||||
-m, --memory uint Memory in MiB (default 2048)
|
||||
--now Start machine now
|
||||
--rootful Whether this machine should prefer rootful container execution
|
||||
--playbook string Run an ansible playbook after first boot
|
||||
--tls-verify Require HTTPS and verify certificates when contacting registries
|
||||
--timezone string Set timezone (default "local")
|
||||
-v, --volume stringArray Volumes to mount, source:target
|
||||
--volume-driver string Optional volume driver
|
||||
*/
|
||||
playbook string
|
||||
cpus *uint
|
||||
@ -38,6 +38,7 @@ type initMachine struct {
|
||||
rootful bool
|
||||
volumes []string
|
||||
userModeNetworking bool
|
||||
tlsVerify *bool
|
||||
|
||||
cmd []string
|
||||
}
|
||||
@ -85,6 +86,9 @@ func (i *initMachine) buildCmd(m *machineTestBuilder) []string {
|
||||
if i.swap != nil {
|
||||
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
|
||||
cmd = append(cmd, name)
|
||||
|
||||
@ -172,6 +176,11 @@ func (i *initMachine) withRunPlaybook(p string) *initMachine {
|
||||
return i
|
||||
}
|
||||
|
||||
func (i *initMachine) withTlsVerify(tlsVerify *bool) *initMachine {
|
||||
i.tlsVerify = tlsVerify
|
||||
return i
|
||||
}
|
||||
|
||||
func (i *initMachine) withUserModeNetworking(r bool) *initMachine { //nolint:unused,nolintlint
|
||||
i.userModeNetworking = r
|
||||
return i
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/image/v5/types"
|
||||
"github.com/containers/podman/v5/pkg/machine/compression"
|
||||
"github.com/containers/podman/v5/pkg/machine/define"
|
||||
"github.com/containers/podman/v5/pkg/machine/ocipull"
|
||||
@ -22,7 +23,9 @@ func pullOCITestDisk(finalDir string, vmType define.VMType) error {
|
||||
return err
|
||||
}
|
||||
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 {
|
||||
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) {
|
||||
var (
|
||||
arch string
|
||||
)
|
||||
func NewOCIArtifactPull(ctx context.Context, dirs *define.MachineDirs, endpoint string, vmName string, vmType define.VMType, finalPath *define.VMFile, skipTlsVerify types.OptionalBool) (*OCIArtifactDisk, error) {
|
||||
var arch string
|
||||
|
||||
artifactVersion := getVersion()
|
||||
switch runtime.GOARCH {
|
||||
@ -108,8 +106,10 @@ func NewOCIArtifactPull(ctx context.Context, dirs *define.MachineDirs, endpoint
|
||||
imageEndpoint: endpoint,
|
||||
machineVersion: artifactVersion,
|
||||
name: vmName,
|
||||
pullOptions: &PullOptions{},
|
||||
vmType: vmType,
|
||||
pullOptions: &PullOptions{
|
||||
SkipTLSVerify: skipTlsVerify,
|
||||
},
|
||||
vmType: vmType,
|
||||
}
|
||||
return &ociDisk, nil
|
||||
}
|
||||
|
@ -4,19 +4,20 @@ import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/image/v5/types"
|
||||
"github.com/containers/podman/v5/pkg/machine/define"
|
||||
"github.com/containers/podman/v5/pkg/machine/ocipull"
|
||||
"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 (
|
||||
err error
|
||||
mydisk ocipull.Disker
|
||||
)
|
||||
|
||||
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 {
|
||||
if strings.HasPrefix(userInputPath, "http") {
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
|
||||
return mydisk.Get()
|
||||
}
|
||||
|
@ -172,7 +172,7 @@ func Init(opts machineDefine.InitOptions, mp vmconfigs.VMProvider) error {
|
||||
// "/path
|
||||
// "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
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user