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) }