kairos-agent/cmd/agent/recovery.go
Ettore Di Giacinto b2e49776a3 Split off cli into separate binaries (#37)
* 🎨 Split off cli into separate binaries

This commit splits off the cli into 3 binaries:
- agent
- cli
- provider

The provider now is a separate component that can be tested by itself
and have its own lifecycle. This paves the way to a ligher c3os variant,
HA support and other features that can be provided on runtime.

This is working, but still there are low hanging fruit to care about.

Fixes #14

* 🤖 Add provider bin to releases

* ⚙️ Handle signals

* ⚙️ Reduce buildsize footprint

* 🎨 Scan for providers also in /system/providers

* 🤖 Run goreleaser

* 🎨 Refactoring
2022-07-04 22:39:34 +02:00

131 lines
3.0 KiB
Go

package main
import (
"context"
"fmt"
"io"
"os/exec"
"time"
"github.com/c3os-io/c3os/internal/c3os"
"github.com/c3os-io/c3os/internal/cmd"
"github.com/c3os-io/c3os/internal/utils"
config "github.com/c3os-io/c3os/pkg/config"
"github.com/ipfs/go-log"
"github.com/urfave/cli"
machine "github.com/c3os-io/c3os/internal/machine"
"github.com/creack/pty"
"github.com/gliderlabs/ssh"
"github.com/mudler/edgevpn/pkg/logger"
"github.com/mudler/edgevpn/pkg/node"
"github.com/mudler/edgevpn/pkg/services"
nodepair "github.com/mudler/go-nodepair"
qr "github.com/mudler/go-nodepair/qrcode"
"github.com/pterm/pterm"
)
const recoveryAddr = "127.0.0.1:2222"
func startRecoveryService(ctx context.Context, token, name, address, loglevel string) error {
nc := config.Network(token, "", loglevel, "c3osrecovery0")
lvl, err := log.LevelFromString(loglevel)
if err != nil {
lvl = log.LevelError
}
llger := logger.New(lvl)
o, _, err := nc.ToOpts(llger)
if err != nil {
llger.Fatal(err.Error())
}
o = append(o,
services.Alive(
time.Duration(20)*time.Second,
time.Duration(10)*time.Second,
time.Duration(10)*time.Second)...)
// opts, err := vpn.Register(vpnOpts...)
// if err != nil {
// return err
// }
o = append(o, services.RegisterService(llger, time.Duration(5*time.Second), name, address)...)
e, err := node.New(o...)
if err != nil {
return err
}
return e.Start(ctx)
}
func recovery(c *cli.Context) error {
cmd.PrintBranding(banner)
tk := nodepair.GenerateToken()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
serviceUUID := utils.RandStringRunes(10)
generatedPassword := utils.RandStringRunes(7)
startRecoveryService(ctx, tk, serviceUUID, recoveryAddr, "fatal")
cmd.PrintTextFromFile(c3os.BrandingFile("recovery_text"), "Recovery")
time.Sleep(5 * time.Second)
pterm.Info.Printfln(
"starting ssh server on '%s', password: '%s' service: '%s' ", recoveryAddr, generatedPassword, serviceUUID)
qr.Print(utils.EncodeRecoveryToken(tk, serviceUUID, generatedPassword))
go sshServer(recoveryAddr, generatedPassword)
// Wait for user input and go back to shell
utils.Prompt("")
cancel()
// give tty1 back
svc, err := machine.Getty(1)
if err == nil {
svc.Start()
}
return nil
}
func sshServer(listenAdddr, password string) {
ssh.Handle(func(s ssh.Session) {
cmd := exec.Command("bash")
ptyReq, winCh, isPty := s.Pty()
if isPty {
cmd.Env = append(cmd.Env, fmt.Sprintf("TERM=%s", ptyReq.Term))
f, err := pty.Start(cmd)
if err != nil {
pterm.Warning.Println("Failed reserving tty")
}
go func() {
for win := range winCh {
setWinsize(f, win.Width, win.Height)
}
}()
go func() {
io.Copy(f, s) // stdin
}()
io.Copy(s, f) // stdout
cmd.Wait()
} else {
io.WriteString(s, "No PTY requested.\n")
s.Exit(1)
}
})
pterm.Info.Println(ssh.ListenAndServe(listenAdddr, nil, ssh.PasswordAuth(func(ctx ssh.Context, pass string) bool {
return pass == password
}),
))
}