mirror of
https://github.com/containers/podman.git
synced 2026-02-05 06:45:31 +01:00
Instead of using rpm-ostree, we now use bootc for os apply. the implementation is a little murky right now and will require some cleanup to implement bootc's transports. for now, we only support oci images from registries. once we have an upgrade command, the transports can be added and the docs for apply can be ammended to be more clear. Fixes: RUN-3836 Signed-off-by: Brent Baude <bbaude@redhat.com>
171 lines
5.4 KiB
Go
171 lines
5.4 KiB
Go
package machine
|
||
|
||
import (
|
||
"bufio"
|
||
"fmt"
|
||
"io"
|
||
"os"
|
||
"os/exec"
|
||
"strconv"
|
||
"strings"
|
||
|
||
"github.com/sirupsen/logrus"
|
||
"golang.org/x/crypto/ssh"
|
||
)
|
||
|
||
// LocalhostSSH is a common function for ssh'ing to a podman machine using system-connections
|
||
// and a port
|
||
// TODO This should probably be taught about an machineconfig to reduce input
|
||
func LocalhostSSH(username, identityPath, name string, sshPort int, inputArgs []string) error {
|
||
return localhostBuiltinSSH(username, identityPath, name, sshPort, inputArgs, true, os.Stdin)
|
||
}
|
||
|
||
// LocalhostSSHShellForceTerm runs the native ssh shell client and forces a terminal (-t)
|
||
func LocalhostSSHShellForceTerm(username, identityPath, name string, sshPort int, inputArgs []string) error {
|
||
return localhostNativeSSH(username, identityPath, name, sshPort, inputArgs, os.Stdin, true)
|
||
}
|
||
|
||
func LocalhostSSHShell(username, identityPath, name string, sshPort int, inputArgs []string) error {
|
||
return localhostNativeSSH(username, identityPath, name, sshPort, inputArgs, os.Stdin, false)
|
||
}
|
||
|
||
func LocalhostSSHSilent(username, identityPath, name string, sshPort int, inputArgs []string) error {
|
||
return localhostBuiltinSSH(username, identityPath, name, sshPort, inputArgs, false, nil)
|
||
}
|
||
|
||
func LocalhostSSHWithStdin(username, identityPath, name string, sshPort int, inputArgs []string, stdin io.Reader) error {
|
||
return localhostBuiltinSSH(username, identityPath, name, sshPort, inputArgs, true, stdin)
|
||
}
|
||
|
||
func localhostBuiltinSSH(username, identityPath, name string, sshPort int, inputArgs []string, passOutput bool, stdin io.Reader) error {
|
||
config, err := createLocalhostConfig(username, identityPath) // WARNING: This MUST NOT be generalized to allow communication over untrusted networks.
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
client, err := ssh.Dial("tcp", fmt.Sprintf("localhost:%d", sshPort), config)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer client.Close()
|
||
|
||
session, err := client.NewSession()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer session.Close()
|
||
|
||
cmd := strings.Join(inputArgs, " ")
|
||
logrus.Debugf("Running ssh command on machine %q: %s", name, cmd)
|
||
session.Stdin = stdin
|
||
if passOutput {
|
||
session.Stdout = os.Stdout
|
||
session.Stderr = os.Stderr
|
||
} else if logrus.IsLevelEnabled(logrus.DebugLevel) {
|
||
return runSessionWithDebug(session, cmd)
|
||
}
|
||
|
||
return session.Run(cmd)
|
||
}
|
||
|
||
func runSessionWithDebug(session *ssh.Session, cmd string) error {
|
||
outPipe, err := session.StdoutPipe()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
errPipe, err := session.StderrPipe()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
logOuput := func(pipe io.Reader, done chan struct{}) {
|
||
scanner := bufio.NewScanner(pipe)
|
||
for scanner.Scan() {
|
||
logrus.Debugf("ssh output: %s", scanner.Text())
|
||
}
|
||
done <- struct{}{}
|
||
}
|
||
if err := session.Start(cmd); err != nil {
|
||
return err
|
||
}
|
||
completed := make(chan struct{}, 2)
|
||
go logOuput(outPipe, completed)
|
||
go logOuput(errPipe, completed)
|
||
<-completed
|
||
<-completed
|
||
|
||
return session.Wait()
|
||
}
|
||
|
||
// createLocalhostConfig returns a *ssh.ClientConfig for authenticating a user using a private key
|
||
//
|
||
// WARNING: This MUST NOT be used to communicate over untrusted networks.
|
||
func createLocalhostConfig(user string, identityPath string) (*ssh.ClientConfig, error) {
|
||
key, err := os.ReadFile(identityPath)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
signer, err := ssh.ParsePrivateKey(key)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return &ssh.ClientConfig{
|
||
// Not specifying ciphers / MACs seems to allow fairly weak ciphers. This config is restricted
|
||
// to connecting to localhost: where we rely on the kernel’s process isolation, not primarily on cryptography.
|
||
User: user,
|
||
Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)},
|
||
// This config is restricted to connecting to localhost (and to a VM we manage),
|
||
// we rely on the kernel’s process isolation, not on cryptography,
|
||
// This would be UNACCEPTABLE for most other uses.
|
||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||
}, nil
|
||
}
|
||
|
||
func localhostNativeSSH(username, identityPath, name string, sshPort int, inputArgs []string, stdin io.Reader, forceTerm bool) error {
|
||
sshDestination := username + "@localhost"
|
||
port := strconv.Itoa(sshPort)
|
||
interactive := true
|
||
|
||
args := append([]string{"-i", identityPath, "-p", port, sshDestination}, LocalhostSSHArgs()...) // WARNING: This MUST NOT be generalized to allow communication over untrusted networks.
|
||
if len(inputArgs) > 0 {
|
||
// on the other condition, the term is forced
|
||
// anyway
|
||
if forceTerm {
|
||
args = append(args, "-t")
|
||
}
|
||
interactive = false
|
||
args = append(args, inputArgs...)
|
||
} else {
|
||
// ensure we have a tty
|
||
args = append(args, "-t")
|
||
fmt.Printf("Connecting to vm %s. To close connection, use `~.` or `exit`\n", name)
|
||
}
|
||
|
||
cmd := exec.Command("ssh", args...)
|
||
logrus.Debugf("Executing: ssh %v\n", args)
|
||
|
||
if err := setupIOPassthrough(cmd, interactive, stdin); err != nil {
|
||
return err
|
||
}
|
||
|
||
return cmd.Run()
|
||
}
|
||
|
||
// LocalhostSSHArgs returns OpenSSH command-line options for connecting with no host key identity checks.
|
||
//
|
||
// WARNING: This MUST NOT be used to communicate over untrusted networks.
|
||
func LocalhostSSHArgs() []string {
|
||
// This config is restricted to connecting to localhost (and to a VM we manage),
|
||
// we rely on the kernel’s process isolation, not on cryptography,
|
||
// This would be UNACCEPTABLE for most other uses.
|
||
return []string{
|
||
"-o", "IdentitiesOnly=yes",
|
||
"-o", "StrictHostKeyChecking=no",
|
||
"-o", "UserKnownHostsFile=" + os.DevNull,
|
||
"-o", "CheckHostIP=no",
|
||
"-o", "LogLevel=ERROR",
|
||
"-o", "SetEnv=LC_ALL=",
|
||
}
|
||
}
|