Skip to content

Commit

Permalink
Implement the first part of 'trust launch'
Browse files Browse the repository at this point in the history
Trust launch creates a new SUDI keyset, creates a machine, provisions
it for that SUDI keyset, then installs it.

Signed-off-by: Serge Hallyn <[email protected]>
  • Loading branch information
hallyn committed Sep 15, 2023
1 parent 257d13f commit fc75c49
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 13 deletions.
93 changes: 93 additions & 0 deletions cmd/trust/launch.go
Original file line number Diff line number Diff line change
@@ -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)
}
5 changes: 4 additions & 1 deletion cmd/trust/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,13 @@ func main() {
// keyset
keysetCmd,

// launch
launchCmd,

// project
projectCmd,

// sudo
// sudi
sudiCmd,

// sign
Expand Down
36 changes: 24 additions & 12 deletions cmd/trust/sudi.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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 {
Expand Down

0 comments on commit fc75c49

Please sign in to comment.