From fc75c49d5daa61072cb11745dc3320d5d023cc04 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Fri, 15 Sep 2023 15:12:08 -0500 Subject: [PATCH] Implement the first part of 'trust launch' Trust launch creates a new SUDI keyset, creates a machine, provisions it for that SUDI keyset, then installs it. Signed-off-by: Serge Hallyn --- cmd/trust/launch.go | 93 +++++++++++++++++++++++++++++++++++++++++++++ cmd/trust/main.go | 5 ++- cmd/trust/sudi.go | 36 ++++++++++++------ 3 files changed, 121 insertions(+), 13 deletions(-) create mode 100644 cmd/trust/launch.go diff --git a/cmd/trust/launch.go b/cmd/trust/launch.go new file mode 100644 index 0000000..244ffe1 --- /dev/null +++ b/cmd/trust/launch.go @@ -0,0 +1,93 @@ +package main + +import ( + "path/filepath" + "strings" + + "github.com/pkg/errors" + "github.com/urfave/cli" +) + +var launchCmd = cli.Command{ + Name: "launch", + Usage: "launch a new machine", + UsageText: `name install-url + name: name to give the VM + install-url: install.json distoci URL to install (e.g. zothub.io/machine/zot/install:1.0.0) + + Note that install is not yet supported.`, + Action: doLaunch, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "project", + Usage: "keyset:project to which this machine will belong (TRUST_PROJECT)", + }, + cli.StringFlag{ + Name: "serial, uuid", + Usage: "Serial number UUID to assign to the machine, empty to use a random UUID", + Value: "", + }, + cli.BoolFlag{ + Name: "skip-provisioning", + Usage: "Skip provisioning the machine", + }, + cli.BoolFlag{ + Name: "skip-install", + Usage: "Skip running the install ISO", + }, + cli.StringFlag{ + Name: "type", + Usage: "Type of machine to launch.", + Value: "kvm", + }, + }, +} + +func splitFullProject(full string) (string, string, error) { + s := strings.Split(full, ":") + if len(s) != 2 { + return "", "", errors.Errorf("Bad project name %q, should be keyset:project, e.g. snakeoil:default.", full) + } + return s[0], s[1], nil +} + +func doLaunch(ctx *cli.Context) error { + args := ctx.Args() + if len(args) < 1 { + return errors.New("A name for the new machine is required") + } + + if !ctx.Bool("skip-install") && len(args) != 2 { + return errors.New("Install manifest URL is required") + } + + mname := args[0] + if mname == "" { + return errors.New("Please specify machine name") + } + + fullProject := ctx.String("project") + keyset, project, err := splitFullProject(fullProject) + if err != nil { + return err + } + + trustDir, err := getMosKeyPath() + if err != nil { + return err + } + + keysetDir := filepath.Join(trustDir, keyset) + projDir := filepath.Join(keysetDir, "manifest", project) + if !PathExists(projDir) { + return errors.Errorf("Project %s not found", fullProject) + } + + uuid := ctx.String("uuid") + sudiPath, err := genSudi(keysetDir, projDir, uuid) + if err != nil { + return errors.Wrapf(err, "Failed generating SUDI") + } + + return errors.Errorf("Created SUDI (%q), now need to launch a machine", sudiPath) +} diff --git a/cmd/trust/main.go b/cmd/trust/main.go index c0cd129..da56a4d 100644 --- a/cmd/trust/main.go +++ b/cmd/trust/main.go @@ -28,10 +28,13 @@ func main() { // keyset keysetCmd, + // launch + launchCmd, + // project projectCmd, - // sudo + // sudi sudiCmd, // sign diff --git a/cmd/trust/sudi.go b/cmd/trust/sudi.go index 06e5824..79de493 100644 --- a/cmd/trust/sudi.go +++ b/cmd/trust/sudi.go @@ -48,8 +48,6 @@ func doGenSudi(ctx *cli.Context) error { var myUUID string if len(args) == 3 { myUUID = args[2] - } else { - myUUID = uuid.NewString() } trustDir, err := getMosKeyPath() @@ -66,37 +64,51 @@ func doGenSudi(ctx *cli.Context) error { return fmt.Errorf("Project not found: %s", projName) } - capath := filepath.Join(keysetPath, "sudi-ca") - snPath := filepath.Join(projPath, "sudi", myUUID) - prodUUID, err := os.ReadFile(filepath.Join(projPath, "uuid")) + if _, err = genSudi(keysetPath, projPath, myUUID); err != nil { + return errors.Wrapf(err, "Failed generating SUDI") + } + + return nil +} + +// Generate a SUDI key for given uuid. If uuid is "", then generate a +// new UUID. Return the directory path for this new SUDI cert. +func genSudi(keysetPath, projDir, sudiUUID string) (string, error) { + prodUUID, err := os.ReadFile(filepath.Join(projDir, "uuid")) if err != nil { - return errors.Wrapf(err, "Failed reading project UUID") + return "", errors.Wrapf(err, "Failed reading project UUID") + } + + if sudiUUID == "" { + sudiUUID = uuid.NewString() } // read the project CA certificate + capath := filepath.Join(keysetPath, "sudi-ca") caCert, err := readCertificateFromFile(filepath.Join(capath, "cert.pem")) if err != nil { - return errors.Wrapf(err, "Failed reading SUDI CA certificate") + return "", errors.Wrapf(err, "Failed reading SUDI CA certificate") } // read the project CA private key to sign the sudi key with caKey, err := readPrivKeyFromFile(filepath.Join(capath, "privkey.pem")) if err != nil { - return errors.Wrapf(err, "Failed reading SUDI CA key") + return "", errors.Wrapf(err, "Failed reading SUDI CA key") } - certTmpl := newCertTemplate(string(prodUUID), myUUID) + certTmpl := newCertTemplate(string(prodUUID), sudiUUID) + snPath := filepath.Join(projDir, "sudi", sudiUUID) if err := trust.EnsureDir(snPath); err != nil { - return errors.Wrapf(err, "Failed creating new SUDI directory") + return "", errors.Wrapf(err, "Failed creating new SUDI directory") } if err := SignCert(&certTmpl, caCert, caKey, snPath); err != nil { os.RemoveAll(snPath) - return errors.Wrapf(err, "Failed creating new SUDI keypair") + return "", errors.Wrapf(err, "Failed creating new SUDI keypair") } - return nil + return snPath, nil } func newCertTemplate(productUUID, machineUUID string) x509.Certificate {