diff --git a/qemu-kernel.go b/qemu-kernel.go index 4865d41..b099671 100644 --- a/qemu-kernel.go +++ b/qemu-kernel.go @@ -14,6 +14,8 @@ import ( "os/exec" "syscall" "time" + + "golang.org/x/crypto/ssh" ) func readBytesUntilEOF(pipe io.ReadCloser) (buf []byte, err error) { @@ -74,8 +76,8 @@ type QemuSystem struct { stderr io.ReadCloser stdout io.ReadCloser } - died bool - sshHostFwd string + died bool + sshAddrPort string // accessible after qemu is closed Stdout, Stderr string @@ -135,15 +137,16 @@ func kvmExists() bool { // Start qemu process func (q *QemuSystem) Start() (err error) { rand.Seed(time.Now().UnixNano()) // Are you sure? - q.sshHostFwd = fmt.Sprintf("hostfwd=tcp:%s-:22", getFreeAddrPort()) + q.sshAddrPort = getFreeAddrPort() + hostfwd := fmt.Sprintf("hostfwd=tcp:%s-:22", q.sshAddrPort) qemuArgs := []string{"-snapshot", "-nographic", "-hda", q.drivePath, "-kernel", q.kernel.Path, - "-append", "root=/dev/sda console=ttyS0 rw", + "-append", "root=/dev/sda ignore_loglevel console=ttyS0 rw", "-smp", fmt.Sprintf("%d", q.Cpus), "-m", fmt.Sprintf("%d", q.Memory), "-device", "e1000,netdev=n1", - "-netdev", "user,id=n1," + q.sshHostFwd, + "-netdev", "user,id=n1," + hostfwd, } if (q.arch == X86_64 || q.arch == I386) && kvmExists() { @@ -197,3 +200,26 @@ func (q *QemuSystem) Stop() { q.cmd.Process.Signal(syscall.SIGKILL) } } + +// Command executes shell commands on qemu system +func (q *QemuSystem) Command(user, cmd string) (output string, err error) { + cfg := &ssh.ClientConfig{ + User: user, + } + cfg.HostKeyCallback = ssh.InsecureIgnoreHostKey() + + client, err := ssh.Dial("tcp", q.sshAddrPort, cfg) + if err != nil { + return + } + defer client.Close() + + session, err := client.NewSession() + if err != nil { + return + } + + bytesOutput, err := session.CombinedOutput(cmd) + output = string(bytesOutput) + return +} diff --git a/qemu-kernel_test.go b/qemu-kernel_test.go index 0ca4b3e..11ed414 100644 --- a/qemu-kernel_test.go +++ b/qemu-kernel_test.go @@ -6,6 +6,7 @@ package qemukernel import ( "net" + "strings" "testing" ) @@ -63,3 +64,39 @@ func TestGetFreeAddrPort(t *testing.T) { } ln.Close() } + +func TestQemuSystemCommand(t *testing.T) { + // FIXME hardcoded kernel path + kernel := Kernel{Name: "Host kernel", Path: "/boot/vmlinuz-4.18.8"} + // FIXME hardcoded qcow2 path + qemu, err := NewQemuSystem(X86_64, kernel, "/home/user/qemu/sid.img") + if err != nil { + t.Fatal(err) + } + + if err = qemu.Start(); err != nil { + t.Fatal(err) + } + defer qemu.Stop() + + output, err := qemu.Command("root", "cat /etc/shadow") + if err != nil { + t.Fatal(err) + } + if !strings.Contains(output, "root::") { + t.Fatal("Wrong output from `cat /etc/shadow` by root") + } + + output, err = qemu.Command("user", "cat /etc/passwd") + if err != nil { + t.Fatal(err) + } + if !strings.Contains(output, "root:x:0:0:root:/root:/bin/bash") { + t.Fatal("Wrong output from `cat /etc/passwd` by user") + } + + output, err = qemu.Command("user", "cat /etc/shadow") + if err == nil { // unsucessful is good because user must not read /etc/shadow + t.Fatal("User have rights for /etc/shadow. WAT?!") + } +}