From f218f8430aa092ec45c4b7868f6d51b0bba4052b Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Wed, 14 Feb 2024 15:00:19 +0100 Subject: [PATCH] machine: implement http proxy logic for all providers Copy all proxy envs into the VM on each start, this allows for updates without having to recrate the VM. This is implemented via shell script that is passed via ssh to the VM after it is started. With that we now use the same logic for all providers the old fw_cfg logic for qemu has been removed and the WSL code as well which keeps the behavior the same. There is a small risk now because we only update the env via ssh that processes started before will have the old incorrect env but it should really only effect core system processes which likely do not need them anyway. The podman system service should not be started at this point so it should be good enough. It also fixes the broken behavior with SSL_CERT_FILE/SSL_CERT_DIR which were updated on each start which is not correct as the files are only copied with ignition so these should not be updated and just set statically when the VM was created. e2e test has been added to ensure the behavior works as expected. Signed-off-by: Paul Holzinger --- pkg/machine/e2e/proxy_test.go | 81 +++++++++++--- pkg/machine/ignition/ignition.go | 134 ++++++++++++----------- pkg/machine/proxyenv/env.go | 57 ++++++++++ pkg/machine/qemu/command/command.go | 5 - pkg/machine/qemu/command/command_test.go | 96 ---------------- pkg/machine/qemu/command/helpers.go | 60 ---------- pkg/machine/qemu/stubber.go | 2 - pkg/machine/shim/host.go | 5 + pkg/machine/ssh.go | 14 ++- pkg/machine/ssh_unix.go | 5 +- pkg/machine/ssh_windows.go | 4 +- pkg/machine/wsl/declares.go | 37 ------- pkg/machine/wsl/machine.go | 40 ------- pkg/machine/wsl/stubber.go | 6 - 14 files changed, 214 insertions(+), 332 deletions(-) create mode 100644 pkg/machine/proxyenv/env.go delete mode 100644 pkg/machine/qemu/command/command_test.go delete mode 100644 pkg/machine/qemu/command/helpers.go diff --git a/pkg/machine/e2e/proxy_test.go b/pkg/machine/e2e/proxy_test.go index eb48e40f99..b83cf4509c 100644 --- a/pkg/machine/e2e/proxy_test.go +++ b/pkg/machine/e2e/proxy_test.go @@ -2,6 +2,7 @@ package e2e_test import ( "os" + "path/filepath" "github.com/containers/podman/v5/pkg/machine/define" . "github.com/onsi/ginkgo/v2" @@ -23,29 +24,30 @@ var _ = Describe("podman machine proxy settings propagation", func() { }) It("ssh to running machine and check proxy settings", func() { - // TODO the proxy test is currently failing on applehv. FIX ME - skipIfVmtype(define.AppleHvVirt, "TODO: this test fails on applehv") + defer func() { + os.Unsetenv("HTTP_PROXY") + os.Unsetenv("HTTPS_PROXY") + os.Unsetenv("SSL_CERT_DIR") + os.Unsetenv("SSL_CERT_FILE") + }() + + certFileDir := GinkgoT().TempDir() + certDir := GinkgoT().TempDir() + certFile := filepath.Join(certFileDir, "cert1") + err := os.WriteFile(certFile, []byte("cert1 content\n"), os.ModePerm) + Expect(err).ToNot(HaveOccurred()) + err = os.WriteFile(filepath.Join(certDir, "cert2"), []byte("cert2 content\n"), os.ModePerm) + Expect(err).ToNot(HaveOccurred()) + + os.Setenv("SSL_CERT_FILE", certFile) + os.Setenv("SSL_CERT_DIR", certDir) - // https://github.com/containers/podman/issues/20129 - if testProvider.VMType() == define.HyperVVirt { - Skip("proxy settings not yet supported") - } name := randomString() i := new(initMachine) session, err := mb.setName(name).setCmd(i.withImagePath(mb.imagePath)).run() Expect(err).ToNot(HaveOccurred()) Expect(session).To(Exit(0)) - defer func() { - httpProxyEnv := os.Getenv("HTTP_PROXY") - httpsProxyEnv := os.Getenv("HTTPS_PROXY") - if httpProxyEnv != "" { - os.Unsetenv("HTTP_PROXY") - } - if httpsProxyEnv != "" { - os.Unsetenv("HTTPS_PROXY") - } - }() proxyURL := "http://abcdefghijklmnopqrstuvwxyz-proxy" os.Setenv("HTTP_PROXY", proxyURL) os.Setenv("HTTPS_PROXY", proxyURL) @@ -65,5 +67,52 @@ var _ = Describe("podman machine proxy settings propagation", func() { Expect(err).ToNot(HaveOccurred()) Expect(sshSession).To(Exit(0)) Expect(sshSession.outputToString()).To(ContainSubstring(proxyURL)) + + // SSL_CERT not implemented for WSL + if !isVmtype(define.WSLVirt) { + sshSession, err = mb.setName(name).setCmd(sshProxy.withSSHCommand([]string{"printenv", "SSL_CERT_DIR", "SSL_CERT_FILE"})).run() + Expect(err).ToNot(HaveOccurred()) + Expect(sshSession).To(Exit(0)) + Expect(string(sshSession.Out.Contents())).To(Equal(define.UserCertsTargetPath + "\n" + define.UserCertsTargetPath + "/cert1" + "\n")) + + sshSession, err = mb.setName(name).setCmd(sshProxy.withSSHCommand([]string{"cat", "$SSL_CERT_DIR/cert2", "$SSL_CERT_FILE"})).run() + Expect(err).ToNot(HaveOccurred()) + Expect(sshSession).To(Exit(0)) + Expect(string(sshSession.Out.Contents())).To(Equal("cert2 content\ncert1 content\n")) + } + + stop := new(stopMachine) + stopSession, err := mb.setName(name).setCmd(stop).run() + Expect(err).ToNot(HaveOccurred()) + Expect(stopSession).To(Exit(0)) + + // Now update proxy env, lets use some special vars to make sure our scripts can handle it + proxy1 := "http:// some special @;\" here" + proxy2 := "https://abc :£$%6 : |\"\"" + os.Setenv("HTTP_PROXY", proxy1) + os.Setenv("HTTPS_PROXY", proxy2) + + // changing SSL_CERT vars should not have an effect + os.Setenv("SSL_CERT_FILE", "/tmp/1") + os.Setenv("SSL_CERT_DIR", "/tmp") + + // start it again should update the proxies + startSession, err = mb.setName(name).setCmd(s).run() + Expect(err).ToNot(HaveOccurred()) + Expect(startSession).To(Exit(0)) + + sshSession, err = mb.setName(name).setCmd(sshProxy.withSSHCommand([]string{"printenv", "HTTP_PROXY", "HTTPS_PROXY"})).run() + Expect(err).ToNot(HaveOccurred()) + Expect(sshSession).To(Exit(0)) + Expect(string(sshSession.Out.Contents())).To(Equal(proxy1 + "\n" + proxy2 + "\n")) + + // SSL_CERT not implemented for WSL + if !isVmtype(define.WSLVirt) { + // SSL_CERT... must still be the same as before + sshSession, err = mb.setName(name).setCmd(sshProxy.withSSHCommand([]string{"cat", "$SSL_CERT_DIR/cert2", "$SSL_CERT_FILE"})).run() + Expect(err).ToNot(HaveOccurred()) + Expect(sshSession).To(Exit(0)) + Expect(string(sshSession.Out.Contents())).To(Equal("cert2 content\ncert1 content\n")) + } }) }) diff --git a/pkg/machine/ignition/ignition.go b/pkg/machine/ignition/ignition.go index 1b337783c5..432d0bb8ee 100644 --- a/pkg/machine/ignition/ignition.go +++ b/pkg/machine/ignition/ignition.go @@ -8,6 +8,7 @@ import ( "io/fs" "net/url" "os" + "path" "path/filepath" "github.com/containers/podman/v5/pkg/machine/define" @@ -178,46 +179,6 @@ ExecStart= ExecStart=-/usr/sbin/agetty --autologin root --noclear %I $TERM ` - // This service gets environment variables that are provided - // through qemu fw_cfg and then sets them into systemd/system.conf.d, - // profile.d and environment.d files - // - // Currently, it is used for propagating - // proxy settings e.g. HTTP_PROXY and others, on a start avoiding - // a need of re-creating/re-initiating a VM - - envset := parser.NewUnitFile() - envset.Add("Unit", "Description", "Environment setter from QEMU FW_CFG") - - envset.Add("Service", "Type", "oneshot") - envset.Add("Service", "RemainAfterExit", "yes") - envset.Add("Service", "Environment", "FWCFGRAW=/sys/firmware/qemu_fw_cfg/by_name/opt/com.coreos/environment/raw") - envset.Add("Service", "Environment", "SYSTEMD_CONF=/etc/systemd/system.conf.d/default-env.conf") - envset.Add("Service", "Environment", "ENVD_CONF=/etc/environment.d/default-env.conf") - envset.Add("Service", "Environment", "PROFILE_CONF=/etc/profile.d/default-env.sh") - envset.Add("Service", "ExecStart", `/usr/bin/bash -c '/usr/bin/test -f ${FWCFGRAW} &&\ - echo "[Manager]\n#Got from QEMU FW_CFG\nDefaultEnvironment=$(/usr/bin/base64 -d ${FWCFGRAW} | sed -e "s+|+ +g")\n" > ${SYSTEMD_CONF} ||\ - echo "[Manager]\n#Got nothing from QEMU FW_CFG\n#DefaultEnvironment=\n" > ${SYSTEMD_CONF}'`) - envset.Add("Service", "ExecStart", `/usr/bin/bash -c '/usr/bin/test -f ${FWCFGRAW} && (\ - echo "#Got from QEMU FW_CFG"> ${ENVD_CONF};\ - IFS="|";\ - for iprxy in $(/usr/bin/base64 -d ${FWCFGRAW}); do\ - echo "$iprxy" >> ${ENVD_CONF}; done ) || \ - echo "#Got nothing from QEMU FW_CFG"> ${ENVD_CONF}'`) - envset.Add("Service", "ExecStart", `/usr/bin/bash -c '/usr/bin/test -f ${FWCFGRAW} && (\ - echo "#Got from QEMU FW_CFG"> ${PROFILE_CONF};\ - IFS="|";\ - for iprxy in $(/usr/bin/base64 -d ${FWCFGRAW}); do\ - echo "export $iprxy" >> ${PROFILE_CONF}; done ) || \ - echo "#Got nothing from QEMU FW_CFG"> ${PROFILE_CONF}'`) - envset.Add("Service", "ExecStartPost", "/usr/bin/systemctl daemon-reload") - - envset.Add("Install", "WantedBy", "sysinit.target") - envsetFile, err := envset.ToString() - if err != nil { - return err - } - ignSystemd := Systemd{ Units: []Unit{ { @@ -261,16 +222,6 @@ ExecStart=-/usr/sbin/agetty --autologin root --noclear %I $TERM }, } - // Only qemu has the qemu firmware environment setting - if ign.VMType == define.QemuVirt { - qemuUnit := Unit{ - Enabled: BoolToPtr(true), - Name: "envset-fwcfg.service", - Contents: &envsetFile, - } - ignSystemd.Units = append(ignSystemd.Units, qemuUnit) - } - if ign.NetRecover { contents, err := GetNetRecoveryUnitFile().ToString() if err != nil { @@ -582,23 +533,29 @@ Delegate=memory pids cpu io certFiles = getCerts(filepath.Join(userHome, ".config/docker/certs.d"), true) files = append(files, certFiles...) - if sslCertFile, ok := os.LookupEnv("SSL_CERT_FILE"); ok { - if _, err := os.Stat(sslCertFile); err == nil { - certFiles = getCerts(sslCertFile, false) + sslCertFileName, ok := os.LookupEnv(sslCertFile) + if ok { + if _, err := os.Stat(sslCertFileName); err == nil { + certFiles = getCerts(sslCertFileName, false) files = append(files, certFiles...) } else { - logrus.Warnf("Invalid path in SSL_CERT_FILE: %q", err) + logrus.Warnf("Invalid path in %s: %q", sslCertFile, err) } } - if sslCertDir, ok := os.LookupEnv("SSL_CERT_DIR"); ok { - if _, err := os.Stat(sslCertDir); err == nil { - certFiles = getCerts(sslCertDir, true) + sslCertDirName, ok := os.LookupEnv(sslCertDir) + if ok { + if _, err := os.Stat(sslCertDirName); err == nil { + certFiles = getCerts(sslCertDirName, true) files = append(files, certFiles...) } else { - logrus.Warnf("Invalid path in SSL_CERT_DIR: %q", err) + logrus.Warnf("Invalid path in %s: %q", sslCertDir, err) } } + if sslCertFileName != "" || sslCertDirName != "" { + // If we copied certs via env then also make the to set the env in the VM. + files = append(files, getSSLEnvironmentFiles(sslCertFileName, sslCertDirName)...) + } files = append(files, File{ Node: Node{ @@ -686,16 +643,18 @@ func getCerts(certsDir string, isDir bool) []File { return files } -func prepareCertFile(path string, name string) (File, error) { - b, err := os.ReadFile(path) +func prepareCertFile(fpath string, name string) (File, error) { + b, err := os.ReadFile(fpath) if err != nil { logrus.Warnf("Unable to read cert file %v", err) return File{}, err } - targetPath := filepath.Join(define.UserCertsTargetPath, name) + // Note path is required here as we always create a path for the linux VM + // even when the client run on windows so we cannot use filepath. + targetPath := path.Join(define.UserCertsTargetPath, name) - logrus.Debugf("Copying cert file from '%s' to '%s'.", path, targetPath) + logrus.Debugf("Copying cert file from '%s' to '%s'.", fpath, targetPath) file := File{ Node: Node{ @@ -714,6 +673,57 @@ func prepareCertFile(path string, name string) (File, error) { return file, nil } +const ( + systemdSSLConf = "/etc/systemd/system.conf.d/podman-machine-ssl.conf" + envdSSLConf = "/etc/environment.d/podman-machine-ssl.conf" + profileSSLConf = "/etc/profile.d/podman-machine-ssl.sh" + sslCertFile = "SSL_CERT_FILE" + sslCertDir = "SSL_CERT_DIR" +) + +func getSSLEnvironmentFiles(sslFileName, sslDirName string) []File { + systemdFileContent := "[Manager]\n" + envdFileContent := "" + profileFileContent := "" + if sslFileName != "" { + // certs are written to UserCertsTargetPath see prepareCertFile() + // Note the mix of path/filepath is intentional and required, we want to get the name of + // a path on the client (i.e. windows) but then join to linux path that will be used inside the VM. + env := fmt.Sprintf("%s=%q\n", sslCertFile, path.Join(define.UserCertsTargetPath, filepath.Base(sslFileName))) + systemdFileContent += "DefaultEnvironment=" + env + envdFileContent += env + profileFileContent += "export " + env + } + if sslDirName != "" { + // certs are written to UserCertsTargetPath see prepareCertFile() + env := fmt.Sprintf("%s=%q\n", sslCertDir, define.UserCertsTargetPath) + systemdFileContent += "DefaultEnvironment=" + env + envdFileContent += env + profileFileContent += "export " + env + } + return []File{ + getSSLFile(systemdSSLConf, systemdFileContent), + getSSLFile(envdSSLConf, envdFileContent), + getSSLFile(profileSSLConf, profileFileContent), + } +} + +func getSSLFile(path, content string) File { + return File{ + Node: Node{ + Group: GetNodeGrp("root"), + Path: path, + User: GetNodeUsr("root"), + }, + FileEmbedded1: FileEmbedded1{ + Contents: Resource{ + Source: EncodeDataURLPtr(content), + }, + Mode: IntToPtr(0644), + }, + } +} + func getLinks(usrName string) []Link { return []Link{{ Node: Node{ diff --git a/pkg/machine/proxyenv/env.go b/pkg/machine/proxyenv/env.go new file mode 100644 index 0000000000..1b358ad72a --- /dev/null +++ b/pkg/machine/proxyenv/env.go @@ -0,0 +1,57 @@ +package proxyenv + +import ( + "fmt" + "io" + "os" + "strings" + + "github.com/containers/common/libnetwork/etchosts" + "github.com/containers/common/pkg/config" + "github.com/containers/podman/v5/pkg/machine" + "github.com/containers/podman/v5/pkg/machine/vmconfigs" + "github.com/sirupsen/logrus" +) + +const proxySetupScriptTemplate = `#!/bin/bash + +SYSTEMD_CONF=/etc/systemd/system.conf.d/default-env.conf +ENVD_CONF=/etc/environment.d/default-env.conf +PROFILE_CONF=/etc/profile.d/default-env.sh + +mkdir -p /etc/profile.d /etc/environment.d /etc/systemd/system.conf.d/ +rm -f $SYSTEMD_CONF $ENVD_CONF $PROFILE_CONF + +echo "[Manager]" >> $SYSTEMD_CONF +for proxy in %s; do + printf "DefaultEnvironment=%%q\n" "$proxy" >> $SYSTEMD_CONF + printf "%%q\n" "$proxy" >> $ENVD_CONF + printf "export %%q\n" "$proxy" >> $PROFILE_CONF +done + +systemctl daemon-reload +` + +func getProxyScript(isWSL bool) io.Reader { + var envs []string + for _, key := range config.ProxyEnv { + if value, ok := os.LookupEnv(key); ok { + // WSL does not use host.containers.internal as valid name for the VM. + if !isWSL { + value = strings.ReplaceAll(value, "127.0.0.1", etchosts.HostContainersInternal) + value = strings.ReplaceAll(value, "localhost", etchosts.HostContainersInternal) + } + // %q to quote the value correctly + envs = append(envs, fmt.Sprintf("%q", key+"="+value)) + } + } + + script := fmt.Sprintf(proxySetupScriptTemplate, strings.Join(envs, " ")) + logrus.Tracef("Final environment variable setup script: %s", script) + return strings.NewReader(script) +} + +func ApplyProxies(mc *vmconfigs.MachineConfig) error { + return machine.CommonSSHWithStdin("root", mc.SSH.IdentityPath, mc.Name, mc.SSH.Port, []string{"/usr/bin/bash"}, + getProxyScript(mc.WSLHypervisor != nil)) +} diff --git a/pkg/machine/qemu/command/command.go b/pkg/machine/qemu/command/command.go index b795d73b41..6d25b19c59 100644 --- a/pkg/machine/qemu/command/command.go +++ b/pkg/machine/qemu/command/command.go @@ -107,11 +107,6 @@ func (q *QemuCmd) SetDisplay(display string) { *q = append(*q, "-display", display) } -// SetPropagatedHostEnvs adds options that propagate SSL and proxy settings -func (q *QemuCmd) SetPropagatedHostEnvs() { - *q = PropagateHostEnv(*q) -} - func (q *QemuCmd) Build() []string { return *q } diff --git a/pkg/machine/qemu/command/command_test.go b/pkg/machine/qemu/command/command_test.go deleted file mode 100644 index 9967d850db..0000000000 --- a/pkg/machine/qemu/command/command_test.go +++ /dev/null @@ -1,96 +0,0 @@ -//go:build !darwin - -package command - -import ( - "encoding/base64" - "fmt" - "strings" - "testing" - - "github.com/containers/common/libnetwork/etchosts" - "github.com/containers/podman/v5/pkg/machine/define" - "github.com/stretchr/testify/assert" -) - -func TestPropagateHostEnv(t *testing.T) { - tests := map[string]struct { - value string - expect string - }{ - "HTTP_PROXY": { - "proxy", - "equal", - }, - "ftp_proxy": { - "domain.com:8888", - "equal", - }, - "FTP_PROXY": { - "proxy", - "equal", - }, - "NO_PROXY": { - "localaddress", - "equal", - }, - "HTTPS_PROXY": { - "", - "unset", - }, - "no_proxy": { - "", - "unset", - }, - "http_proxy": { - "127.0.0.1:8888", - fmt.Sprintf("%s:8888", etchosts.HostContainersInternal), - }, - "https_proxy": { - "localhost:8888", - fmt.Sprintf("%s:8888", etchosts.HostContainersInternal), - }, - "SSL_CERT_FILE": { - "/some/f=oo.cert", - fmt.Sprintf("%s/f=oo.cert", define.UserCertsTargetPath), - }, - "SSL_CERT_DIR": { - "/some/my/certs", - define.UserCertsTargetPath, - }, - } - - for key, item := range tests { - t.Setenv(key, item.value) - } - - cmdLine := PropagateHostEnv(make([]string, 0)) - - assert.Len(t, cmdLine, 2) - assert.Equal(t, "-fw_cfg", cmdLine[0]) - tokens := strings.Split(cmdLine[1], ",string=") - decodeString, err := base64.StdEncoding.DecodeString(tokens[1]) - assert.NoError(t, err) - - // envsRawArr looks like: ["BAR=\"bar\"", "FOO=\"foo\""] - envsRawArr := strings.Split(string(decodeString), "|") - // envs looks like: {"BAR": "bar", "FOO": "foo"} - envs := make(map[string]string) - for _, env := range envsRawArr { - key, value, _ := strings.Cut(env, "=") - envs[key] = strings.Trim(value, "\"") - } - - for key, test := range tests { - switch test.expect { - case "equal": - assert.Equal(t, envs[key], test.value) - case "unset": - if _, ok := envs[key]; ok { - t.Errorf("env %s should not be set", key) - } - default: - assert.Equal(t, envs[key], test.expect) - } - } -} diff --git a/pkg/machine/qemu/command/helpers.go b/pkg/machine/qemu/command/helpers.go deleted file mode 100644 index c763770b9b..0000000000 --- a/pkg/machine/qemu/command/helpers.go +++ /dev/null @@ -1,60 +0,0 @@ -//go:build !darwin - -package command - -import ( - "encoding/base64" - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/containers/common/libnetwork/etchosts" - "github.com/containers/common/pkg/config" - "github.com/containers/podman/v5/pkg/machine/define" -) - -func GetProxyVariables() map[string]string { - proxyOpts := make(map[string]string) - for _, variable := range config.ProxyEnv { - if value, ok := os.LookupEnv(variable); ok { - if value == "" { - continue - } - - v := strings.ReplaceAll(value, "127.0.0.1", etchosts.HostContainersInternal) - v = strings.ReplaceAll(v, "localhost", etchosts.HostContainersInternal) - proxyOpts[variable] = v - } - } - return proxyOpts -} - -// PropagateHostEnv is here for providing the ability to propagate -// proxy and SSL settings (e.g. HTTP_PROXY and others) on a start -// and avoid a need of re-creating/re-initiating a VM -func PropagateHostEnv(cmdLine QemuCmd) QemuCmd { - varsToPropagate := make([]string, 0) - - for k, v := range GetProxyVariables() { - varsToPropagate = append(varsToPropagate, fmt.Sprintf("%s=%q", k, v)) - } - - if sslCertFile, ok := os.LookupEnv("SSL_CERT_FILE"); ok { - pathInVM := filepath.Join(define.UserCertsTargetPath, filepath.Base(sslCertFile)) - varsToPropagate = append(varsToPropagate, fmt.Sprintf("%s=%q", "SSL_CERT_FILE", pathInVM)) - } - - if _, ok := os.LookupEnv("SSL_CERT_DIR"); ok { - varsToPropagate = append(varsToPropagate, fmt.Sprintf("%s=%q", "SSL_CERT_DIR", define.UserCertsTargetPath)) - } - - if len(varsToPropagate) > 0 { - prefix := "name=opt/com.coreos/environment,string=" - envVarsJoined := strings.Join(varsToPropagate, "|") - fwCfgArg := prefix + base64.StdEncoding.EncodeToString([]byte(envVarsJoined)) - return append(cmdLine, "-fw_cfg", fwCfgArg) - } - - return cmdLine -} diff --git a/pkg/machine/qemu/stubber.go b/pkg/machine/qemu/stubber.go index bcb8803e59..3f5d340d11 100644 --- a/pkg/machine/qemu/stubber.go +++ b/pkg/machine/qemu/stubber.go @@ -170,8 +170,6 @@ func (q *QEMUStubber) StartVM(mc *vmconfigs.MachineConfig) (func() error, func() cmdLine := q.Command - cmdLine.SetPropagatedHostEnvs() - // Disable graphic window when not in debug mode // Done in start, so we're not suck with the debug level we used on init if !logrus.IsLevelEnabled(logrus.DebugLevel) { diff --git a/pkg/machine/shim/host.go b/pkg/machine/shim/host.go index 03a979af2d..6d94ad6f65 100644 --- a/pkg/machine/shim/host.go +++ b/pkg/machine/shim/host.go @@ -12,6 +12,7 @@ import ( "github.com/containers/podman/v5/pkg/machine/connection" machineDefine "github.com/containers/podman/v5/pkg/machine/define" "github.com/containers/podman/v5/pkg/machine/ignition" + "github.com/containers/podman/v5/pkg/machine/proxyenv" "github.com/containers/podman/v5/pkg/machine/vmconfigs" "github.com/containers/podman/v5/utils" "github.com/hashicorp/go-multierror" @@ -414,6 +415,10 @@ func Start(mc *vmconfigs.MachineConfig, mp vmconfigs.VMProvider, _ *machineDefin return errors.New(msg) } + if err := proxyenv.ApplyProxies(mc); err != nil { + return err + } + // mount the volumes to the VM if err := mp.MountVolumesToVM(mc, opts.Quiet); err != nil { return err diff --git a/pkg/machine/ssh.go b/pkg/machine/ssh.go index 7a66984cf2..de5fd9aee8 100644 --- a/pkg/machine/ssh.go +++ b/pkg/machine/ssh.go @@ -2,6 +2,8 @@ package machine import ( "fmt" + "io" + "os" "os/exec" "strconv" @@ -12,14 +14,18 @@ import ( // and a port // TODO This should probably be taught about an machineconfig to reduce input func CommonSSH(username, identityPath, name string, sshPort int, inputArgs []string) error { - return commonSSH(username, identityPath, name, sshPort, inputArgs, false) + return commonSSH(username, identityPath, name, sshPort, inputArgs, false, os.Stdin) } func CommonSSHSilent(username, identityPath, name string, sshPort int, inputArgs []string) error { - return commonSSH(username, identityPath, name, sshPort, inputArgs, true) + return commonSSH(username, identityPath, name, sshPort, inputArgs, true, os.Stdin) } -func commonSSH(username, identityPath, name string, sshPort int, inputArgs []string, silent bool) error { +func CommonSSHWithStdin(username, identityPath, name string, sshPort int, inputArgs []string, stdin io.Reader) error { + return commonSSH(username, identityPath, name, sshPort, inputArgs, false, stdin) +} + +func commonSSH(username, identityPath, name string, sshPort int, inputArgs []string, silent bool, stdin io.Reader) error { sshDestination := username + "@localhost" port := strconv.Itoa(sshPort) interactive := true @@ -40,7 +46,7 @@ func commonSSH(username, identityPath, name string, sshPort int, inputArgs []str logrus.Debugf("Executing: ssh %v\n", args) if !silent { - if err := setupIOPassthrough(cmd, interactive); err != nil { + if err := setupIOPassthrough(cmd, interactive, stdin); err != nil { return err } } diff --git a/pkg/machine/ssh_unix.go b/pkg/machine/ssh_unix.go index 17e5acd06f..620969814d 100644 --- a/pkg/machine/ssh_unix.go +++ b/pkg/machine/ssh_unix.go @@ -3,12 +3,13 @@ package machine import ( + "io" "os" "os/exec" ) -func setupIOPassthrough(cmd *exec.Cmd, interactive bool) error { - cmd.Stdin = os.Stdin +func setupIOPassthrough(cmd *exec.Cmd, interactive bool, stdin io.Reader) error { + cmd.Stdin = stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr diff --git a/pkg/machine/ssh_windows.go b/pkg/machine/ssh_windows.go index 3440dc1687..cf44023ff1 100644 --- a/pkg/machine/ssh_windows.go +++ b/pkg/machine/ssh_windows.go @@ -8,8 +8,8 @@ import ( "github.com/sirupsen/logrus" ) -func setupIOPassthrough(cmd *exec.Cmd, interactive bool) error { - cmd.Stdin = os.Stdin +func setupIOPassthrough(cmd *exec.Cmd, interactive bool, stdin io.Reader) error { + cmd.Stdin = stdin if interactive { cmd.Stdout = os.Stdout diff --git a/pkg/machine/wsl/declares.go b/pkg/machine/wsl/declares.go index fe0061218e..a1f13ae310 100644 --- a/pkg/machine/wsl/declares.go +++ b/pkg/machine/wsl/declares.go @@ -175,43 +175,6 @@ SocketMode=0660 SocketGroup=wheel ` -const proxyConfigSetup = `#!/bin/bash - -SYSTEMD_CONF=/etc/systemd/system.conf.d/default-env.conf -ENVD_CONF=/etc/environment.d/default-env.conf -PROFILE_CONF=/etc/profile.d/default-env.sh - -IFS="|" -read proxies - -mkdir -p /etc/profile.d /etc/environment.d /etc/systemd/system.conf.d/ -rm -f $SYSTEMD_CONF -for proxy in $proxies; do - output+="$proxy " -done -echo "[Manager]" >> $SYSTEMD_CONF -echo -ne "DefaultEnvironment=" >> $SYSTEMD_CONF - -echo $output >> $SYSTEMD_CONF -rm -f $ENVD_CONF -for proxy in $proxies; do - echo "$proxy" >> $ENVD_CONF -done -rm -f $PROFILE_CONF -for proxy in $proxies; do - echo "export $proxy" >> $PROFILE_CONF -done -` - -const proxyConfigAttempt = `if [ -f /usr/local/bin/proxyinit ]; \ -then /usr/local/bin/proxyinit; \ -else exit 42; \ -fi` - -const clearProxySettings = `rm -f /etc/systemd/system.conf.d/default-env.conf \ - /etc/environment.d/default-env.conf \ - /etc/profile.d/default-env.sh` - const wslInstallError = `Could not %s. See previous output for any potential failure details. If you can not resolve the issue, and rerunning fails, try the "wsl --install" process outlined in the following article: diff --git a/pkg/machine/wsl/machine.go b/pkg/machine/wsl/machine.go index f869bc8b65..26f16a66ec 100644 --- a/pkg/machine/wsl/machine.go +++ b/pkg/machine/wsl/machine.go @@ -244,41 +244,6 @@ func setupPodmanDockerSock(dist string, rootful bool) error { return nil } -func configureProxy(dist string, useProxy bool, quiet bool) error { - if !useProxy { - _ = wslInvoke(dist, "sh", "-c", clearProxySettings) - return nil - } - var content string - for i, key := range config.ProxyEnv { - if value, _ := os.LookupEnv(key); len(value) > 0 { - var suffix string - if i < (len(config.ProxyEnv) - 1) { - suffix = "|" - } - content = fmt.Sprintf("%s%s=\"%s\"%s", content, key, value, suffix) - } - } - - if err := wslPipe(content, dist, "sh", "-c", proxyConfigAttempt); err != nil { - const failMessage = "Failure creating proxy configuration" - if exitErr, isExit := err.(*exec.ExitError); isExit && exitErr.ExitCode() != 42 { - return fmt.Errorf("%v: %w", failMessage, err) - } - if !quiet { - fmt.Println("Installing proxy support") - } - _ = wslPipe(proxyConfigSetup, dist, "sh", "-c", - "cat > /usr/local/bin/proxyinit; chmod 755 /usr/local/bin/proxyinit") - - if err = wslPipe(content, dist, "/usr/local/bin/proxyinit"); err != nil { - return fmt.Errorf("%v: %w", failMessage, err) - } - } - - return nil -} - func enableUserLinger(mc *vmconfigs.MachineConfig, dist string) error { lingerCmd := "mkdir -p /var/lib/systemd/linger; touch /var/lib/systemd/linger/" + mc.SSH.RemoteUsername if err := wslInvoke(dist, "sh", "-c", lingerCmd); err != nil { @@ -317,11 +282,6 @@ func installScripts(dist string) error { return fmt.Errorf("could not create bootstrap script for guest OS: %w", err) } - if err := wslPipe(proxyConfigSetup, dist, "sh", "-c", - "cat > /usr/local/bin/proxyinit; chmod 755 /usr/local/bin/proxyinit"); err != nil { - return fmt.Errorf("could not create proxyinit script for guest OS: %w", err) - } - return nil } diff --git a/pkg/machine/wsl/stubber.go b/pkg/machine/wsl/stubber.go index fb58b3ca6a..e1d6b483d3 100644 --- a/pkg/machine/wsl/stubber.go +++ b/pkg/machine/wsl/stubber.go @@ -204,14 +204,8 @@ func (w WSLStubber) PostStartNetworking(mc *vmconfigs.MachineConfig, noInfo bool } func (w WSLStubber) StartVM(mc *vmconfigs.MachineConfig) (func() error, func() error, error) { - useProxy := setupWslProxyEnv() dist := machine.ToDist(mc.Name) - // TODO Quiet is hard set to false: follow up - if err := configureProxy(dist, useProxy, false); err != nil { - return nil, nil, err - } - // TODO The original code checked to see if the SSH port was actually open and re-assigned if it was // we could consider this but it should be higher up the stack // if !machine.IsLocalPortAvailable(v.Port) {