mirror of
https://github.com/containers/podman.git
synced 2025-10-15 02:06:42 +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/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
|
||||
}
|
||||
|
@ -288,18 +288,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)
|
||||
})
|
||||
})
|
||||
|
||||
@ -339,7 +360,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()
|
||||
@ -367,9 +388,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))
|
||||
|
||||
@ -377,7 +406,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
|
||||
@ -387,13 +420,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
|
||||
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))
|
||||
|
@ -20,10 +20,10 @@ type initMachine struct {
|
||||
--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,7 +106,9 @@ func NewOCIArtifactPull(ctx context.Context, dirs *define.MachineDirs, endpoint
|
||||
imageEndpoint: endpoint,
|
||||
machineVersion: artifactVersion,
|
||||
name: vmName,
|
||||
pullOptions: &PullOptions{},
|
||||
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