go-qemu/main.go

315 lines
6.6 KiB
Go

package main
import (
"bufio"
"bytes"
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"os"
"os/exec"
"path/filepath"
"github.com/google/uuid"
"github.com/melbahja/goph"
"github.com/rayaman/go-qemu/pkg/v1/image"
"github.com/rayaman/go-qemu/pkg/v1/image/formats"
"github.com/rayaman/go-qemu/pkg/v1/system"
"github.com/rayaman/go-qemu/pkg/v1/types"
"github.com/rayaman/go-qemu/pkg/v1/types/accel"
"github.com/rayaman/go-qemu/pkg/v1/types/arch"
"github.com/rayaman/go-qemu/pkg/v1/types/boot"
"github.com/rayaman/go-qemu/pkg/v1/types/disk"
"github.com/rayaman/go-qemu/pkg/v1/types/memory"
"github.com/rayaman/go-qemu/pkg/v1/types/nic"
"github.com/rayaman/go-qemu/pkg/v1/types/smp"
"github.com/shirou/gopsutil/v3/process"
"golang.org/x/crypto/ssh"
"sigs.k8s.io/yaml"
)
type Credentials struct {
User string `json:"user"`
Pass string `json:"pass"`
MachineID string `json:"machine_id"`
PublicKeyPath string `json:"public_key_path"`
PrivateKeyPath string `json:"private_key_path"`
}
type Controller struct {
cancel context.CancelFunc
cmd *exec.Cmd
err []error
}
func (c *Controller) Stop() {
c.cancel()
}
func (c *Controller) Errors() []error {
return c.err
}
func KillProcess(name string) error {
processes, err := process.Processes()
if err != nil {
return err
}
for _, p := range processes {
n, err := p.Name()
if err != nil {
return err
}
if n == name {
return p.Kill()
}
}
return fmt.Errorf("process not found")
}
func StartMachine(machine system.Machine) *Controller {
ctrl := &Controller{}
go func() {
ctx, cfunc := context.WithCancel(context.TODO())
ctrl.cancel = cfunc
var stdout bytes.Buffer
var stderr bytes.Buffer
cmd := exec.Command(fmt.Sprintf("qemu-system-%v", machine.Arch), machine.Expand()...)
cmd.Stdout = &stderr
cmd.Stderr = &stdout
ctrl.cmd = cmd
err := cmd.Start()
if err != nil {
fmt.Println("Error:", err)
}
go func() {
err := cmd.Wait()
if err != nil {
fmt.Println("Error:", err)
}
cfunc()
}()
for {
select {
case <-ctx.Done():
fmt.Println("Killing Process")
err := cmd.Process.Kill()
if err != nil {
fmt.Println("Error:", err)
}
return
}
}
}()
return ctrl
}
func main() {
// var img image.Image = &formats.QCOW2{
// ImageName: "imgs/UbuntuTest.img",
// BackingFile: "base/UbuntuBase.img",
// BackingFmt: "qcow2",
// }
// var img image.Image = &formats.QCOW2{
// ImageName: "imgs/NetworkTest.img",
// }
// err := image.Create(img, types.GetSize(types.GB, 8), image.Options{IsBaseImage: true})
// if err != nil {
// panic(err)
// }
machine := system.Machine{
Arch: arch.X86_64,
Cores: smp.SMP{
Cpus: 4,
},
Boot: boot.Boot{
Order: []boot.Drives{boot.HARDDISK},
Menu: types.Off,
},
Memory: memory.Memory{
Size: 4096,
},
Accel: accel.Accel{
Accelerator: accel.GetAccelerator(arch.X86_64),
},
Nic: nic.NIC{
Type: nic.TAP,
Options: nic.Options{
Tap: &nic.TapOptions{
IFName: "qemu-tap",
},
},
},
NoGraphic: types.Set,
HardDiskA: `./imgs/UbuntuTest.img`,
}
data, err := yaml.Marshal(machine)
if err != nil {
panic(err)
}
// Write data to a file
err = os.WriteFile("machine.yaml", data, 0644)
if err != nil {
panic(err)
}
data, err = os.ReadFile("machine.yaml")
if err != nil {
panic(err)
}
mac := system.Machine{}
err = yaml.Unmarshal(data, &mac)
if err != nil {
panic(err)
}
fmt.Println(machine.Expand())
// fmt.Println(mac.Expand())
fmt.Println(accel.GetAccelerator(arch.X86_64))
os.Exit(0)
ctrl := StartMachine(machine)
fmt.Println("Press enter to stop machine")
reader := bufio.NewReader(os.Stdin)
_, _ = reader.ReadString('\n')
ctrl.Stop()
fmt.Println("Press enter to exit")
_, _ = reader.ReadString('\n')
// Start new ssh connection with private key.
//client, err := goph.New("baseimguser", "127.0.0.1", goph.Password("food99"))
// _, err := SetUpMachine()
// if err != nil {
// panic(err)
// }
// auth, err := goph.Key("keys/3e6e04c3-1af4-41e7-8984-c60012034075", "")
// if err != nil {
// panic(err)
// }
// client, err := goph.NewConn(&goph.Config{
// User: "baseimguser",
// Addr: "127.0.0.1",
// Port: 8888,
// Auth: auth,
// Timeout: goph.DefaultTimeout,
// Callback: ssh.InsecureIgnoreHostKey(),
// })
// if err != nil {
// panic(err)
// }
// defer client.Close()
// out, err := client.Run(`echo 'baseimgpass' | sudo -S ls /tmp`)
// if err != nil {
// panic(err)
// }
// fmt.Println(string(out))
// SetUpMachine(types.GetSize(types.GB, 64))
}
func SetUpMachine(size disk.Size) (*Credentials, error) {
machine_id := uuid.New().String()
private_path := filepath.Join("keys", machine_id)
public_path := filepath.Join("keys", machine_id+".pub")
err := MakeSSHKeyPair(public_path, private_path)
if err != nil {
return nil, err
}
img := &formats.QCOW2{
ImageName: fmt.Sprintf("imgs/%v.img", machine_id),
BackingFile: "base/UbuntuBase.img",
BackingFmt: "qcow2",
}
err = image.Create(img, size)
if err != nil {
return nil, err
}
// ToDo start Machine
Config := &Credentials{
MachineID: machine_id,
PublicKeyPath: public_path,
PrivateKeyPath: private_path,
}
client, err := goph.NewConn(&goph.Config{
User: "baseimguser",
Addr: "127.0.0.1",
Port: 8888,
Auth: goph.Password("baseimgpass"),
Timeout: goph.DefaultTimeout,
Callback: ssh.InsecureIgnoreHostKey(),
})
if err != nil {
return nil, err
}
defer client.Close()
err = client.Upload(public_path, "/home/baseimguser/.ssh/authorized_keys")
if err != nil {
return nil, err
}
cmds := []string{
// Allow us to use our private key to connect directly into root user
`cp /home/baseimguser/.ssh/authorized_keys /root/.ssh/authorized_keys`,
}
for _, cmd := range cmds {
out, err := client.Run(`echo 'baseimgpass' | sudo -S ` + cmd)
if err != nil {
return nil, fmt.Errorf("Got Error: %v --- %v", string(out), err)
}
}
return Config, nil
}
func MakeSSHKeyPair(pubKeyPath, privateKeyPath string) error {
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return err
}
privateKeyFile, err := os.Create(privateKeyPath)
defer privateKeyFile.Close()
if err != nil {
return err
}
privateKeyPEM := &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey)}
if err := pem.Encode(privateKeyFile, privateKeyPEM); err != nil {
return err
}
pub, err := ssh.NewPublicKey(&privateKey.PublicKey)
if err != nil {
return err
}
return os.WriteFile(pubKeyPath, ssh.MarshalAuthorizedKey(pub), 0655)
}