diff --git a/cmd/mosb/mkboot.go b/cmd/mosb/mkboot.go index d54c5f7..95878bb 100644 --- a/cmd/mosb/mkboot.go +++ b/cmd/mosb/mkboot.go @@ -9,6 +9,7 @@ import ( "github.com/apex/log" "github.com/pkg/errors" "github.com/project-machine/mos/pkg/mosconfig" + "github.com/project-machine/mos/pkg/utils" "github.com/urfave/cli" ) @@ -58,7 +59,7 @@ func doMkBoot(ctx *cli.Context) error { defer os.RemoveAll(tmpd) cachedir := filepath.Join(tmpd, "cache") - mosconfig.EnsureDir(cachedir) + utils.EnsureDir(cachedir) ociboot := mosconfig.OciBoot{ KeySet: s[0], Project: s[1], diff --git a/cmd/mosctl/activate.go b/cmd/mosctl/activate.go index 4a9c12e..3fd38f8 100644 --- a/cmd/mosctl/activate.go +++ b/cmd/mosctl/activate.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/project-machine/mos/pkg/mosconfig" + "github.com/project-machine/mos/pkg/utils" "github.com/urfave/cli" ) @@ -32,7 +33,7 @@ var activateCmd = cli.Command{ func doActivate(ctx *cli.Context) error { rfs := ctx.String("root") - if rfs == "" || !mosconfig.PathExists(rfs) { + if rfs == "" || !utils.PathExists(rfs) { return fmt.Errorf("A valid root directory must be specified") } diff --git a/cmd/mosctl/initrdsetup.go b/cmd/mosctl/initrdsetup.go index d651b4e..78950b6 100644 --- a/cmd/mosctl/initrdsetup.go +++ b/cmd/mosctl/initrdsetup.go @@ -3,8 +3,8 @@ package main import ( "fmt" - "github.com/project-machine/mos/pkg/mosconfig" "github.com/project-machine/mos/pkg/trust" + "github.com/project-machine/mos/pkg/utils" "github.com/urfave/cli" ) @@ -15,7 +15,7 @@ var initrdSetupCmd = cli.Command{ } func doInitrdSetup(ctx *cli.Context) error { - if !mosconfig.PathExists("/dev/tpm0") { + if !utils.PathExists("/dev/tpm0") { return fmt.Errorf("No TPM. No other subsystems have been implemented") } diff --git a/cmd/mosctl/install.go b/cmd/mosctl/install.go index 9d1bf58..a2e2452 100644 --- a/cmd/mosctl/install.go +++ b/cmd/mosctl/install.go @@ -16,6 +16,7 @@ import ( "github.com/pkg/errors" "github.com/project-machine/mos/pkg/mosconfig" "github.com/project-machine/mos/pkg/trust" + "github.com/project-machine/mos/pkg/utils" "github.com/urfave/cli" ) @@ -205,7 +206,7 @@ func makeFileSystemEFI(path string, ssize int) error { cmd = append(cmd, fmt.Sprintf("-S%d", ssize)) } cmd = append(cmd, path) - return mosconfig.RunCommand(cmd...) + return utils.RunCommand(cmd...) } func makeFileSystemEXT4(path, label string) error { @@ -249,7 +250,7 @@ type MkExt4Opts struct { } func MkExt4FS(path string, opts MkExt4Opts) error { - conf, err := mosconfig.WriteTempFile("/tmp", "mkfsconf-", mke2fsConf) + conf, err := utils.WriteTempFile("/tmp", "mkfsconf-", mke2fsConf) if err != nil { return err } @@ -270,7 +271,8 @@ func MkExt4FS(path string, opts MkExt4Opts) error { } cmd = append(cmd, path) - return mosconfig.RunCommandEnv(append(os.Environ(), "MKE2FS_CONFIG="+conf), cmd...) + env := []string{"MKE2FS_CONFIG=" + conf} + return utils.RunCommandEnv(cmd, env) } func sortedPartNums(pSet *disko.PartitionSet) []uint { @@ -316,7 +318,7 @@ func wipeDiskParts(disk disko.Disk, skipPart func(disko.Disk, uint) bool) error return errors.Wrap(err, "Failed to close a filehandle?") } - if err := mosconfig.RunCommand("udevadm", "settle"); err != nil { + if err := utils.RunCommand("udevadm", "settle"); err != nil { log.Warnf("Failed udevadm settle after wipingParts") } @@ -476,7 +478,7 @@ func findInstallDisk(disks disko.DiskSet) (disko.Disk, error) { // Obviously this should become a lot more flexible. What's here // suffices for enabling/testing the core functionality. func doPartition(opts mosconfig.InstallOpts) error { - luksKey, err := mosconfig.ReadKeyFromUserKeyring("machine:luks") + luksKey, err := utils.ReadKeyFromUserKeyring("machine:luks") if err != nil { return err } @@ -554,7 +556,7 @@ func doPartition(opts mosconfig.InstallOpts) error { return errors.Wrapf(err, "Failed creating EFI partition") } dest := filepath.Join(opts.RFS, "boot/efi") - if err := mosconfig.EnsureDir(dest); err != nil { + if err := utils.EnsureDir(dest); err != nil { return errors.Wrapf(err, "Failed creating boot/EFI") } if err := syscall.Mount(efiPath, dest, "vfat", 0, ""); err != nil { @@ -566,7 +568,7 @@ func doPartition(opts mosconfig.InstallOpts) error { if err := makeFileSystemEXT4(configPath, configPart); err != nil { return errors.Wrapf(err, "Failed creating ext4 on %s", configPath) } - if err := mosconfig.EnsureDir(opts.ConfigDir); err != nil { + if err := utils.EnsureDir(opts.ConfigDir); err != nil { return errors.Wrapf(err, "Failed creating mount path %s", opts.ConfigDir) } if err := syscall.Mount(configPath, opts.ConfigDir, "ext4", 0, ""); err != nil { @@ -579,7 +581,7 @@ func doPartition(opts mosconfig.InstallOpts) error { if err := makeFileSystemEXT4(storePath, storePart); err != nil { return errors.Wrapf(err, "Failed creating ext4 on %s", storePath) } - if err := mosconfig.EnsureDir(opts.StoreDir); err != nil { + if err := utils.EnsureDir(opts.StoreDir); err != nil { return errors.Wrapf(err, "Failed creating mount path %s", opts.StoreDir) } if err := syscall.Mount(storePath, opts.StoreDir, "ext4", 0, ""); err != nil { @@ -617,13 +619,13 @@ func doInstall(ctx *cli.Context) error { return errors.Wrapf(err, "Failed partitioning") } } else { - if !mosconfig.PathExists(opts.CaPath) { + if !utils.PathExists(opts.CaPath) { return errors.Errorf("Install manifest CA (%s) missing", opts.CaPath) } - if !mosconfig.PathExists(opts.ConfigDir) { + if !utils.PathExists(opts.ConfigDir) { return errors.Errorf("Configuration directory (%s) missing", opts.ConfigDir) } - if !mosconfig.PathExists(opts.StoreDir) { + if !utils.PathExists(opts.StoreDir) { return errors.Errorf("Storage cache dir (%s) missing", opts.StoreDir) } } diff --git a/cmd/mosctl/mount.go b/cmd/mosctl/mount.go index c859995..6588e12 100644 --- a/cmd/mosctl/mount.go +++ b/cmd/mosctl/mount.go @@ -5,6 +5,7 @@ import ( "github.com/apex/log" "github.com/project-machine/mos/pkg/mosconfig" + "github.com/project-machine/mos/pkg/utils" "github.com/urfave/cli" ) @@ -36,7 +37,7 @@ var mountCmd = cli.Command{ func doMount(ctx *cli.Context) error { rfs := ctx.String("root") - if rfs == "" || !mosconfig.PathExists(rfs) { + if rfs == "" || !utils.PathExists(rfs) { return fmt.Errorf("A valid root directory must be specified") } diff --git a/cmd/mosctl/preinstall.go b/cmd/mosctl/preinstall.go index 03077d6..80c7f25 100644 --- a/cmd/mosctl/preinstall.go +++ b/cmd/mosctl/preinstall.go @@ -3,8 +3,8 @@ package main import ( "fmt" - "github.com/project-machine/mos/pkg/mosconfig" "github.com/project-machine/mos/pkg/trust" + "github.com/project-machine/mos/pkg/utils" "github.com/urfave/cli" ) @@ -15,7 +15,7 @@ var preInstallCmd = cli.Command{ } func doPreInstall(ctx *cli.Context) error { - if !mosconfig.PathExists("/dev/tpm0") { + if !utils.PathExists("/dev/tpm0") { return fmt.Errorf("No TPM. No other subsystems have been implemented") } diff --git a/cmd/mosctl/provision.go b/cmd/mosctl/provision.go index 2958b56..b84f843 100644 --- a/cmd/mosctl/provision.go +++ b/cmd/mosctl/provision.go @@ -4,8 +4,8 @@ import ( "fmt" "github.com/apex/log" - "github.com/project-machine/mos/pkg/mosconfig" "github.com/project-machine/mos/pkg/trust" + "github.com/project-machine/mos/pkg/utils" "github.com/urfave/cli" ) @@ -33,7 +33,7 @@ func doProvision(ctx *cli.Context) error { log.Warnf("No disk specified. No disk will be provisioned") } - if !mosconfig.PathExists("/dev/tpm0") { + if !utils.PathExists("/dev/tpm0") { return fmt.Errorf("No TPM. No other subsystems have been implemented") } diff --git a/cmd/mosctl/update.go b/cmd/mosctl/update.go index 918938d..92607d7 100644 --- a/cmd/mosctl/update.go +++ b/cmd/mosctl/update.go @@ -5,6 +5,7 @@ import ( "path/filepath" "github.com/project-machine/mos/pkg/mosconfig" + "github.com/project-machine/mos/pkg/utils" "github.com/urfave/cli" ) @@ -23,7 +24,7 @@ var updateCmd = cli.Command{ func doUpdate(ctx *cli.Context) error { rfs := ctx.String("root") - if rfs == "" || !mosconfig.PathExists(rfs) { + if rfs == "" || !utils.PathExists(rfs) { return fmt.Errorf("A valid root directory must be specified") } diff --git a/cmd/trust/initrdsetup.go b/cmd/trust/initrdsetup.go index d72a6fe..78950b6 100644 --- a/cmd/trust/initrdsetup.go +++ b/cmd/trust/initrdsetup.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/project-machine/mos/pkg/trust" + "github.com/project-machine/mos/pkg/utils" "github.com/urfave/cli" ) @@ -14,7 +15,7 @@ var initrdSetupCmd = cli.Command{ } func doInitrdSetup(ctx *cli.Context) error { - if !PathExists("/dev/tpm0") { + if !utils.PathExists("/dev/tpm0") { return fmt.Errorf("No TPM. No other subsystems have been implemented") } diff --git a/cmd/trust/keyset.go b/cmd/trust/keyset.go index 556083c..b79f09e 100644 --- a/cmd/trust/keyset.go +++ b/cmd/trust/keyset.go @@ -16,6 +16,7 @@ import ( "github.com/project-machine/mos/pkg/mosconfig" tree "github.com/project-machine/mos/pkg/printdirtree" "github.com/project-machine/mos/pkg/trust" + "github.com/project-machine/mos/pkg/utils" "github.com/urfave/cli" ) @@ -102,12 +103,12 @@ func initkeyset(keysetName string, Org []string) error { return errors.New("keyset parameter is missing") } - moskeysetPath, err := getMosKeyPath() + moskeysetPath, err := utils.GetMosKeyPath() if err != nil { return err } keysetPath := filepath.Join(moskeysetPath, keysetName) - if PathExists(keysetPath) { + if utils.PathExists(keysetPath) { return fmt.Errorf("%s keyset already exists", keysetName) } @@ -191,11 +192,11 @@ func initkeyset(keysetName string, Org []string) error { // Generate sample uuid, manifest key and cert mName := filepath.Join(keysetPath, "manifest", "default") - if err = trust.EnsureDir(mName); err != nil { + if err = utils.EnsureDir(mName); err != nil { return errors.Wrapf(err, "Failed creating default project directory") } sName := filepath.Join(mName, "sudi") - if err = trust.EnsureDir(sName); err != nil { + if err = utils.EnsureDir(sName); err != nil { return errors.Wrapf(err, "Failed creating default sudi directory") } @@ -337,13 +338,13 @@ func doAddKeyset(ctx *cli.Context) error { } // See if keyset exists - mosKeyPath, err := getMosKeyPath() + mosKeyPath, err := utils.GetMosKeyPath() if err != nil { return err } keysetPath := filepath.Join(mosKeyPath, keysetName) - if PathExists(keysetPath) { + if utils.PathExists(keysetPath) { return fmt.Errorf("%s keyset already exists", keysetName) } @@ -381,7 +382,7 @@ func doListKeysets(ctx *cli.Context) error { if len(ctx.Args()) != 0 { return fmt.Errorf("Wrong number of arguments (please see \"--help\")") } - moskeysetPath, err := getMosKeyPath() + moskeysetPath, err := utils.GetMosKeyPath() if err != nil { return err } @@ -407,13 +408,13 @@ func doShowKeyset(ctx *cli.Context) error { return fmt.Errorf("Please specify keyset name. Select from 'trust keyset list'") } - moskeysetPath, err := getMosKeyPath() + moskeysetPath, err := utils.GetMosKeyPath() if err != nil { return err } keysetPath := filepath.Join(moskeysetPath, keysetName) - if !PathExists(keysetPath) { + if !utils.PathExists(keysetPath) { return fmt.Errorf("Unknown keyset '%s', cannot find keyset at path: %q", keysetName, keysetPath) } @@ -453,14 +454,14 @@ func doShowKeyset(ctx *cli.Context) error { } keyPath := filepath.Join(keysetPath, keyName) - if !PathExists(keyPath) { + if !utils.PathExists(keyPath) { return fmt.Errorf("Keyset %s key %q does not exist at %q", keysetName, keyName, keyPath) } if len(ctx.Args()) > 2 { item := ctx.Args()[2] fullPath := filepath.Join(keyPath, item) - if !PathExists(fullPath) { + if !utils.PathExists(fullPath) { return fmt.Errorf("Failed reading keyset %s key %s item %s at %q: %w", keysetName, keyName, item, fullPath, err) } @@ -524,10 +525,10 @@ func doAddPCR7data(ctx *cli.Context) error { if limited == "" || tpm == "" || prod == "" { return errors.New("PCR7 values are missing") } - if !PathExists(limited) || !PathExists(tpm) || !PathExists(prod) { + if !utils.PathExists(limited) || !utils.PathExists(tpm) || !utils.PathExists(prod) { return errors.New("Some PCR7 files do not exist") } - if !PathExists(passwdPolicy) || !PathExists(luksPolicy) { + if !utils.PathExists(passwdPolicy) || !utils.PathExists(luksPolicy) { return errors.New("Some policy digest files do not exist") } @@ -577,13 +578,13 @@ func buildProvisioner(keysetName string) error { return nil } - moskeysetPath, err := getMosKeyPath() + moskeysetPath, err := utils.GetMosKeyPath() if err != nil { return err } keyPath := filepath.Join(moskeysetPath, keysetName) outfile := filepath.Join(keyPath, "artifacts", "provision.iso") - trust.EnsureDir(filepath.Dir(outfile)) + utils.EnsureDir(filepath.Dir(outfile)) if err := mosconfig.BuildProvisioner(keysetName, "default", outfile); err != nil { return errors.Wrapf(err, "Failed to create provisioning ISO") @@ -599,13 +600,13 @@ func buildInstaller(keysetName string) error { return nil } - moskeysetPath, err := getMosKeyPath() + moskeysetPath, err := utils.GetMosKeyPath() if err != nil { return err } keyPath := filepath.Join(moskeysetPath, keysetName) outfile := filepath.Join(keyPath, "artifacts", "install.iso") - if err := trust.EnsureDir(filepath.Dir(outfile)); err != nil { + if err := utils.EnsureDir(filepath.Dir(outfile)); err != nil { return errors.Wrapf(err, "Failed creating %q", outfile) } diff --git a/cmd/trust/launch.go b/cmd/trust/launch.go index b1d9287..d8212ce 100644 --- a/cmd/trust/launch.go +++ b/cmd/trust/launch.go @@ -8,7 +8,7 @@ import ( "github.com/apex/log" "github.com/pkg/errors" "github.com/project-machine/mos/pkg/provider" - "github.com/project-machine/mos/pkg/trust" + "github.com/project-machine/mos/pkg/utils" "github.com/urfave/cli" ) @@ -97,14 +97,14 @@ func doLaunch(ctx *cli.Context) error { return err } - trustDir, err := getMosKeyPath() + trustDir, err := utils.GetMosKeyPath() if err != nil { return err } keysetDir := filepath.Join(trustDir, keyset) projDir := filepath.Join(keysetDir, "manifest", project) - if !PathExists(projDir) { + if !utils.PathExists(projDir) { return errors.Errorf("Project %s not found", fullProject) } @@ -163,10 +163,10 @@ func makeSudiVfat(sudiDir string) error { cert := filepath.Join(sudiDir, "cert.pem") key := filepath.Join(sudiDir, "privkey.pem") disk := filepath.Join(sudiDir, "sudi.vfat") - if !trust.PathExists(cert) || !trust.PathExists(key) { + if !utils.PathExists(cert) || !utils.PathExists(key) { return errors.Errorf("cert or key does not exist") } - if trust.PathExists(disk) { + if utils.PathExists(disk) { return errors.Errorf("sudi.vfat already exists") } @@ -179,13 +179,13 @@ func makeSudiVfat(sudiDir string) error { if err := os.Truncate(disk, 20*1024*1024); err != nil { return errors.Wrapf(err, "Failed truncating sudi disk") } - if err := trust.RunCommand("mkfs.vfat", "-n", "trust-data", disk); err != nil { + if err := utils.RunCommand("mkfs.vfat", "-n", "trust-data", disk); err != nil { return errors.Wrapf(err, "Failed formatting sudi disk") } - if err := trust.RunCommand("mcopy", "-i", disk, cert, "::cert.pem"); err != nil { + if err := utils.RunCommand("mcopy", "-i", disk, cert, "::cert.pem"); err != nil { return errors.Wrapf(err, "Failed copying cert to sudi disk") } - if err := trust.RunCommand("mcopy", "-i", disk, key, "::privkey.pem"); err != nil { + if err := utils.RunCommand("mcopy", "-i", disk, key, "::privkey.pem"); err != nil { return errors.Wrapf(err, "Failed copying key to sudi disk") } @@ -198,7 +198,7 @@ func makeInstallVFAT(sudiDir, url string) error { } disk := filepath.Join(sudiDir, "install.vfat") - if trust.PathExists(disk) { + if utils.PathExists(disk) { return errors.Errorf("%q already exists", disk) } @@ -223,10 +223,10 @@ func makeInstallVFAT(sudiDir, url string) error { if err = os.Truncate(disk, 20*1024*1024); err != nil { return errors.Wrapf(err, "Failed truncating install disk") } - if err = trust.RunCommand("mkfs.vfat", "-n", "inst-data", disk); err != nil { + if err = utils.RunCommand("mkfs.vfat", "-n", "inst-data", disk); err != nil { return errors.Wrapf(err, "Failed formatting install disk") } - if err = trust.RunCommand("mcopy", "-i", disk, urlfile, "::url.txt"); err != nil { + if err = utils.RunCommand("mcopy", "-i", disk, urlfile, "::url.txt"); err != nil { return errors.Wrapf(err, "Failed copying urlfile to install disk") } diff --git a/cmd/trust/preinstall.go b/cmd/trust/preinstall.go index 89764c2..80c7f25 100644 --- a/cmd/trust/preinstall.go +++ b/cmd/trust/preinstall.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/project-machine/mos/pkg/trust" + "github.com/project-machine/mos/pkg/utils" "github.com/urfave/cli" ) @@ -14,7 +15,7 @@ var preInstallCmd = cli.Command{ } func doPreInstall(ctx *cli.Context) error { - if !PathExists("/dev/tpm0") { + if !utils.PathExists("/dev/tpm0") { return fmt.Errorf("No TPM. No other subsystems have been implemented") } diff --git a/cmd/trust/project.go b/cmd/trust/project.go index 40b2819..e8c3df1 100644 --- a/cmd/trust/project.go +++ b/cmd/trust/project.go @@ -8,7 +8,7 @@ import ( "path/filepath" "github.com/pkg/errors" - "github.com/project-machine/mos/pkg/trust" + "github.com/project-machine/mos/pkg/utils" "github.com/urfave/cli" ) @@ -40,14 +40,14 @@ func doAddProject(ctx *cli.Context) error { keysetName := args[0] projName := args[1] - trustDir, err := getMosKeyPath() + trustDir, err := utils.GetMosKeyPath() if err != nil { return err } keysetPath := filepath.Join(trustDir, keysetName) projPath := filepath.Join(keysetPath, "manifest", projName) - if PathExists(projPath) { + if utils.PathExists(projPath) { return fmt.Errorf("Project %s already exists", projName) } @@ -62,7 +62,7 @@ func doAddProject(ctx *cli.Context) error { return errors.Wrapf(err, "Failed creating new project") } - if err := trust.EnsureDir(filepath.Join(projPath, "sudi")); err != nil { + if err := utils.EnsureDir(filepath.Join(projPath, "sudi")); err != nil { os.RemoveAll(projPath) return errors.Wrapf(err, "Failed creating sudi directory for new project") } @@ -78,17 +78,17 @@ func doListProjects(ctx *cli.Context) error { } keysetName := args[0] - trustDir, err := getMosKeyPath() + trustDir, err := utils.GetMosKeyPath() if err != nil { return err } keysetPath := filepath.Join(trustDir, keysetName) - if !PathExists(keysetPath) { + if !utils.PathExists(keysetPath) { return fmt.Errorf("Keyset not found: %s", keysetName) } keysetPath = filepath.Join(keysetPath, "manifest") - if !PathExists(keysetPath) { + if !utils.PathExists(keysetPath) { fmt.Printf("No projects found") return nil } diff --git a/cmd/trust/provision.go b/cmd/trust/provision.go index 3233467..b84f843 100644 --- a/cmd/trust/provision.go +++ b/cmd/trust/provision.go @@ -5,6 +5,7 @@ import ( "github.com/apex/log" "github.com/project-machine/mos/pkg/trust" + "github.com/project-machine/mos/pkg/utils" "github.com/urfave/cli" ) @@ -32,7 +33,7 @@ func doProvision(ctx *cli.Context) error { log.Warnf("No disk specified. No disk will be provisioned") } - if !PathExists("/dev/tpm0") { + if !utils.PathExists("/dev/tpm0") { return fmt.Errorf("No TPM. No other subsystems have been implemented") } diff --git a/cmd/trust/sign.go b/cmd/trust/sign.go index 1a00e04..2dd357a 100644 --- a/cmd/trust/sign.go +++ b/cmd/trust/sign.go @@ -5,6 +5,7 @@ import ( "github.com/pkg/errors" "github.com/project-machine/mos/pkg/trust" + "github.com/project-machine/mos/pkg/utils" "github.com/urfave/cli" ) @@ -47,7 +48,7 @@ func doSignEFI(ctx *cli.Context) error { } // Make sure efibinary exists - if !PathExists(efibinary) { + if !utils.PathExists(efibinary) { return fmt.Errorf("%s does not exist", efibinary) } @@ -60,11 +61,11 @@ func doSignEFI(ctx *cli.Context) error { } // Make sure the key and cert exists - if !PathExists(key) { + if !utils.PathExists(key) { return fmt.Errorf("%s does not exist", key) } - if !PathExists(cert) { + if !utils.PathExists(cert) { return fmt.Errorf("%s does not exist", cert) } diff --git a/cmd/trust/sudi.go b/cmd/trust/sudi.go index 79de493..f982aa8 100644 --- a/cmd/trust/sudi.go +++ b/cmd/trust/sudi.go @@ -10,7 +10,7 @@ import ( "github.com/google/uuid" "github.com/pkg/errors" - "github.com/project-machine/mos/pkg/trust" + "github.com/project-machine/mos/pkg/utils" "github.com/urfave/cli" ) @@ -50,17 +50,17 @@ func doGenSudi(ctx *cli.Context) error { myUUID = args[2] } - trustDir, err := getMosKeyPath() + trustDir, err := utils.GetMosKeyPath() if err != nil { return err } keysetPath := filepath.Join(trustDir, keysetName) - if !PathExists(keysetPath) { + if !utils.PathExists(keysetPath) { return fmt.Errorf("Keyset not found: %s", keysetName) } projPath := filepath.Join(keysetPath, "manifest", projName) - if !PathExists(projPath) { + if !utils.PathExists(projPath) { return fmt.Errorf("Project not found: %s", projName) } @@ -99,7 +99,7 @@ func genSudi(keysetPath, projDir, sudiUUID string) (string, error) { certTmpl := newCertTemplate(string(prodUUID), sudiUUID) snPath := filepath.Join(projDir, "sudi", sudiUUID) - if err := trust.EnsureDir(snPath); err != nil { + if err := utils.EnsureDir(snPath); err != nil { return "", errors.Wrapf(err, "Failed creating new SUDI directory") } @@ -131,17 +131,17 @@ func doListSudi(ctx *cli.Context) error { } keysetName := args[0] projName := args[1] - trustDir, err := getMosKeyPath() + trustDir, err := utils.GetMosKeyPath() if err != nil { return err } keysetPath := filepath.Join(trustDir, keysetName) - if !PathExists(keysetPath) { + if !utils.PathExists(keysetPath) { return fmt.Errorf("Keyset not found: %s", keysetName) } projPath := filepath.Join(keysetPath, "manifest", projName) - if !PathExists(projPath) { + if !utils.PathExists(projPath) { return fmt.Errorf("Project not found: %s", projName) } diff --git a/cmd/trust/utils.go b/cmd/trust/utils.go index d5e5288..1c646ed 100644 --- a/cmd/trust/utils.go +++ b/cmd/trust/utils.go @@ -20,28 +20,11 @@ import ( "github.com/google/uuid" "github.com/pkg/errors" "github.com/project-machine/mos/pkg/trust" + "github.com/project-machine/mos/pkg/utils" ) -// PathExists checks for existense of specified path -func PathExists(d string) bool { - _, err := os.Stat(d) - if err != nil && os.IsNotExist(err) { - return false - } - return true -} - -func getTrustPath() (string, error) { - configDir, err := os.UserConfigDir() - if err != nil { - return "", err - } - path := filepath.Join(configDir, "machine", "trust") - return path, os.MkdirAll(path, 0755) -} - func getSudiDir() (string, error) { - dataDir, err := UserDataDir() + dataDir, err := utils.UserDataDir() if err != nil { return "", err } @@ -49,40 +32,13 @@ func getSudiDir() (string, error) { return sudiPath, os.MkdirAll(sudiPath, 0755) } -// UserDataDir returns the user's data directory -func UserDataDir() (string, error) { - p, err := os.UserHomeDir() - if err != nil { - return "", err - } - return filepath.Join(p, ".local", "share"), nil -} - -// ConfPath returns the user's config directory -func ConfPath(cluster string) string { - configDir, err := os.UserConfigDir() - if err != nil { - return "" - } - return filepath.Join(configDir, "machine", cluster, "machine.yaml") -} - -// Get the location where keysets are stored -func getMosKeyPath() (string, error) { - dataDir, err := UserDataDir() - if err != nil { - return "", err - } - return filepath.Join(dataDir, "machine", "trust", "keys"), nil -} - func KeysetExists(keysetname string) bool { - mosKeyPath, err := getMosKeyPath() + mosKeyPath, err := utils.GetMosKeyPath() if err != nil { return false } keysetPath := filepath.Join(mosKeyPath, keysetname) - if PathExists(keysetPath) { + if utils.PathExists(keysetPath) { return true } else { return false @@ -92,7 +48,7 @@ func KeysetExists(keysetname string) bool { // SignCert creates a CA signed certificate and keypair in destdir func SignCert(template, CAcert *x509.Certificate, CAkey any, destdir string) error { // Check if credentials already exist - if PathExists(filepath.Join(destdir, "privkey.pem")) { + if utils.PathExists(filepath.Join(destdir, "privkey.pem")) { return fmt.Errorf("credentials already exist in %s", destdir) } @@ -202,19 +158,19 @@ func readPrivKeyFromFile(keypath string) (any, error) { func getCA(CAname, keysetName string) (*x509.Certificate, any, error) { // locate the keyset - keysetPath, err := getMosKeyPath() + keysetPath, err := utils.GetMosKeyPath() if err != nil { return nil, nil, err } keysetPath = filepath.Join(keysetPath, keysetName) - if !PathExists(keysetPath) { + if !utils.PathExists(keysetPath) { return nil, nil, fmt.Errorf("keyset %s, does not exist", keysetName) } CAcert, err := readCertificateFromFile(filepath.Join(keysetPath, CAname, "cert.pem")) // See if the CA exists CApath := filepath.Join(keysetPath, CAname) - if !PathExists(CApath) { + if !utils.PathExists(CApath) { return nil, nil, fmt.Errorf("%s CA does not exist", CAname) } @@ -492,12 +448,12 @@ func addPcr7data(keysetName string, pdata pcr7Data) error { if keysetName == "" { return errors.New("Please specify a keyset name") } - moskeypath, err = getMosKeyPath() + moskeypath, err = utils.GetMosKeyPath() if err != nil { return err } keysetPath := filepath.Join(moskeypath, keysetName) - if !PathExists(keysetPath) { + if !utils.PathExists(keysetPath) { return fmt.Errorf("The keyset, %s, does not exist.", keysetName) } @@ -512,7 +468,7 @@ func addPcr7data(keysetName string, pdata pcr7Data) error { // Create pcr7data directory if it does not exist. // Its ok if pcr7data dir already exists. We might be adding additional signdata pcr7dataPath := filepath.Join(keysetPath, "pcr7data/policy-2") - if !PathExists(pcr7dataPath) { + if !utils.PathExists(pcr7dataPath) { err = os.MkdirAll(keysetPath, 0750) if err != nil { return err @@ -531,8 +487,8 @@ func addPcr7data(keysetName string, pdata pcr7Data) error { // Check to see if public keys already exist for this keyset, if not // then extract public keys and save them to pcr7data dir. pcr7dataPubkeys := filepath.Join(pcr7dataPath, "pubkeys") - if !PathExists(pcr7dataPubkeys) { - if err = trust.EnsureDir(pcr7dataPubkeys); err != nil { + if !utils.PathExists(pcr7dataPubkeys) { + if err = utils.EnsureDir(pcr7dataPubkeys); err != nil { return errors.New("Failed to create directory for public keys") } } @@ -574,7 +530,7 @@ func addPcr7data(keysetName string, pdata pcr7Data) error { return err } indexdir := filepath.Join(pcr7dataPath, pcrIndex[0:2], pcrIndex[2:]) - if err = trust.EnsureDir(indexdir); err != nil { + if err = utils.EnsureDir(indexdir); err != nil { return err } diff --git a/cmd/trust/verify.go b/cmd/trust/verify.go index f3ea867..e6215fd 100644 --- a/cmd/trust/verify.go +++ b/cmd/trust/verify.go @@ -5,6 +5,7 @@ import ( "github.com/pkg/errors" "github.com/project-machine/mos/pkg/trust" + "github.com/project-machine/mos/pkg/utils" "github.com/urfave/cli" ) @@ -39,7 +40,7 @@ func doVerifyEFI(ctx *cli.Context) error { } // Make sure efibinary exists - if !PathExists(signedefibinary) { + if !utils.PathExists(signedefibinary) { return fmt.Errorf("%s does not exist", signedefibinary) } @@ -50,7 +51,7 @@ func doVerifyEFI(ctx *cli.Context) error { } // Make sure the cert exists - if !PathExists(cert) { + if !utils.PathExists(cert) { return fmt.Errorf("%s does not exist", cert) } diff --git a/pkg/mosconfig/disk.go b/pkg/mosconfig/disk.go index f58736c..706452f 100644 --- a/pkg/mosconfig/disk.go +++ b/pkg/mosconfig/disk.go @@ -13,6 +13,7 @@ import ( "github.com/anuvu/disko/partid" "github.com/apex/log" "github.com/pkg/errors" + "github.com/project-machine/mos/pkg/utils" "github.com/rekby/gpt" "golang.org/x/text/encoding/unicode" "golang.org/x/text/transform" @@ -79,7 +80,7 @@ func fetchDiskCandidates() ([]string, error) { existing := []string{} for _, p := range candidates { - if PathExists(p) { + if utils.PathExists(p) { existing = append(existing, p) } } @@ -185,7 +186,7 @@ func EspBootPartition() (string, int, error) { func efiClearBootEntries() error { args := []string{"efibootmgr", "-v"} - stdout, stderr, rc := RunCommandWithOutputErrorRc(args...) + stdout, stderr, rc := utils.RunCommandWithOutputErrorRc(args...) if rc != 0 { return errors.Errorf("Error querying existing boot entries:\nstdout: %s\nstderr: %s", stdout, stderr) } @@ -200,18 +201,18 @@ func efiClearBootEntries() error { continue } log.Infof("Removing boot entry %d (%s)", num, l) - err = RunCommand("efibootmgr", "--bootnum", l[4:8], "--delete-bootnum") + err = utils.RunCommand("efibootmgr", "--bootnum", l[4:8], "--delete-bootnum") if err != nil { log.Warnf("(Probably ok) Error removing boot entry: %s", l) } } const enoent = "No such file or directory" - err := RunCommand("efibootmgr", "--delete-bootnext") + err := utils.RunCommand("efibootmgr", "--delete-bootnext") if err != nil && err.Error() != enoent { log.Warnf("(Probably ok) Error running efibootmgr delete-bootnext: '%s'", err.Error()) } - err = RunCommand("efibootmgr", "--delete-bootorder") + err = utils.RunCommand("efibootmgr", "--delete-bootorder") if err != nil && err.Error() != enoent { log.Warnf("(Probably ok) Error running efibootmgr delete-bootorder: '%s'", err.Error()) } @@ -252,5 +253,5 @@ func WriteBootEntry() error { efiPart := fmt.Sprintf("%d", efiPartNum) cmd := []string{"efibootmgr", "-c", "-d", bootDisk, "-p", efiPart, "-L", "mosboot", "-l", shimPath, "-u", "--append-binary-args", "-"} - return RunWithStdin(string(kname), cmd...) + return utils.RunWithStdin(string(kname), cmd...) } diff --git a/pkg/mosconfig/distspec.go b/pkg/mosconfig/distspec.go index 80b1258..2103105 100644 --- a/pkg/mosconfig/distspec.go +++ b/pkg/mosconfig/distspec.go @@ -14,6 +14,7 @@ import ( digest "github.com/opencontainers/go-digest" ispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" + "github.com/project-machine/mos/pkg/utils" ) const ( @@ -334,7 +335,7 @@ func (disturl *DistUrl) Post(path string) (int64, digest.Digest, error) { // I think requiring a proper install makes sense at that point. // Which means this is only used for livecds. func (mos *Mos) remoteManifest(url string) (*InstallFile, error) { - if PathExists(filepath.Join(mos.opts.ConfigDir, "manifest.git")) { + if utils.PathExists(filepath.Join(mos.opts.ConfigDir, "manifest.git")) { return &InstallFile{}, errors.Errorf("Opening remote manifest on installed host is unsupported") } diff --git a/pkg/mosconfig/init.go b/pkg/mosconfig/init.go index f043def..b65e920 100644 --- a/pkg/mosconfig/init.go +++ b/pkg/mosconfig/init.go @@ -9,13 +9,14 @@ import ( "path/filepath" "github.com/apex/log" + "github.com/project-machine/mos/pkg/utils" ) func systemdStart(unitName string) error { - if err := RunCommand("systemctl", "enable", unitName); err != nil { + if err := utils.RunCommand("systemctl", "enable", unitName); err != nil { return fmt.Errorf("Failed enabling %s: %w", unitName, err) } - if err := RunCommand("systemctl", "start", "--no-block", unitName); err != nil { + if err := utils.RunCommand("systemctl", "start", "--no-block", unitName); err != nil { return fmt.Errorf("Failed starting %s: %w", unitName, err) } return nil diff --git a/pkg/mosconfig/install.go b/pkg/mosconfig/install.go index ad29a84..31f03ad 100644 --- a/pkg/mosconfig/install.go +++ b/pkg/mosconfig/install.go @@ -19,6 +19,7 @@ import ( "github.com/opencontainers/umoci" "github.com/pkg/errors" "github.com/project-machine/mos/pkg/trust" + "github.com/project-machine/mos/pkg/utils" "github.com/urfave/cli" "gopkg.in/yaml.v2" @@ -197,7 +198,7 @@ func (mos *Mos) InstallNewBoot(boot Target) error { } defer cleanup() - mounted, err := IsMountpoint("/boot/efi") + mounted, err := utils.IsMountpoint("/boot/efi") if err != nil { return errors.Wrapf(err, "Failed checking whether /boot/efi is mounted") } @@ -208,30 +209,30 @@ func (mos *Mos) InstallNewBoot(boot Target) error { os.RemoveAll("/boot/efi/EFI/BOOT.BAK") defer func() { - if PathExists("/boot/efi/EFI/BOOT.BAK") { + if utils.PathExists("/boot/efi/EFI/BOOT.BAK") { os.RemoveAll("/boot/efi/EFI/BOOT") if err := os.Rename("/boot/efi/EFI/BOOT.BAK", "/boot/efi/EFI/BOOT"); err != nil { log.Warnf("Failed restoring boot") } } }() - if PathExists("/boot/efi/EFI/BOOT") { + if utils.PathExists("/boot/efi/EFI/BOOT") { err := os.Rename("/boot/efi/EFI/BOOT", "/boot/efi/EFI/BOOT.BAK") if err != nil { return errors.Wrapf(err, "Failed backing up boot directory") } } - if err := EnsureDir("/boot/efi/EFI/BOOT"); err != nil { + if err := utils.EnsureDir("/boot/efi/EFI/BOOT"); err != nil { return errors.Wrapf(err, "Failed creating target boot directory") } src := filepath.Join(mp, "bootkit", "shim.efi") dest := "/boot/efi/EFI/BOOT/shim.efi" - if err := CopyFileBits(src, dest); err != nil { + if err := utils.CopyFileBits(src, dest); err != nil { return errors.Wrapf(err, "Failed copying shim into boot directory") } src = filepath.Join(mp, "bootkit", "kernel.efi") dest = "/boot/efi/EFI/BOOT/kernel.efi" - if err := CopyFileBits(src, dest); err != nil { + if err := utils.CopyFileBits(src, dest); err != nil { return errors.Wrapf(err, "Failed copying UKI into boot directory") } diff --git a/pkg/mosconfig/manifest.go b/pkg/mosconfig/manifest.go index 484fb3a..3d7c11f 100644 --- a/pkg/mosconfig/manifest.go +++ b/pkg/mosconfig/manifest.go @@ -16,17 +16,18 @@ import ( "github.com/go-git/go-git/v5/plumbing/object" "github.com/opencontainers/umoci" "github.com/pkg/errors" + "github.com/project-machine/mos/pkg/utils" ) // Only used during first install. Create a new $config/manifest.git/ func (mos *Mos) initManifest(manifestPath, manifestCert, manifestCA, configPath string) error { - shaSum, err := ShaSum(manifestPath) + shaSum, err := utils.ShaSum(manifestPath) if err != nil { return fmt.Errorf("Failed calculating shasum: %w", err) } dir := filepath.Join(configPath, "manifest.git") - if PathExists(dir) { + if utils.PathExists(dir) { return fmt.Errorf("manifest already exists, chickening out!") } @@ -52,7 +53,7 @@ func (mos *Mos) initManifest(manifestPath, manifestCert, manifestCA, configPath sFile := fmt.Sprintf("%s.json.signed", shaSum) dest := filepath.Join(dir, sFile) - err = CopyFileBits(manifestPath+".signed", dest) + err = utils.CopyFileBits(manifestPath+".signed", dest) if err != nil { return fmt.Errorf("Failed copying install manifest: %w", err) } @@ -63,7 +64,7 @@ func (mos *Mos) initManifest(manifestPath, manifestCert, manifestCA, configPath mFile := fmt.Sprintf("%s.json", shaSum) dest = filepath.Join(dir, mFile) - err = CopyFileBits(manifestPath, dest) + err = utils.CopyFileBits(manifestPath, dest) if err != nil { return fmt.Errorf("Failed copying install manifest: %w", err) } @@ -74,7 +75,7 @@ func (mos *Mos) initManifest(manifestPath, manifestCert, manifestCA, configPath pFile := fmt.Sprintf("%s.pem", shaSum) dest = filepath.Join(dir, pFile) - err = CopyFileBits(manifestCert, dest) + err = utils.CopyFileBits(manifestCert, dest) if err != nil { return fmt.Errorf("Failed copying manifest Cert: %w", err) } @@ -300,7 +301,7 @@ func (mos *Mos) UpdateManifest(manifest *SysManifest, newmanifest *SysManifest, // Copy any needed source jsons into our tempdir for _, t := range manifest.SysTargets { f := t.Source - if PathExists(filepath.Join(newdir, f)) { + if utils.PathExists(filepath.Join(newdir, f)) { continue } base := strings.TrimSuffix(f, ".json") @@ -308,7 +309,7 @@ func (mos *Mos) UpdateManifest(manifest *SysManifest, newmanifest *SysManifest, fName := base + ext src := filepath.Join(mPath, fName) dest := filepath.Join(newdir, fName) - if err := CopyFileBits(src, dest); err != nil { + if err := utils.CopyFileBits(src, dest); err != nil { return fmt.Errorf("Failed copying %q out of system manifest repo: %w", src, err) } } @@ -324,7 +325,7 @@ func (mos *Mos) UpdateManifest(manifest *SysManifest, newmanifest *SysManifest, // And copy back the files we need for _, t := range newmanifest.SysTargets { f := t.Source - if PathExists(filepath.Join(mPath, f)) { + if utils.PathExists(filepath.Join(mPath, f)) { continue } base := strings.TrimSuffix(f, ".json") @@ -332,7 +333,7 @@ func (mos *Mos) UpdateManifest(manifest *SysManifest, newmanifest *SysManifest, fName := base + ext src := filepath.Join(newdir, fName) dest := filepath.Join(mPath, fName) - if err := CopyFileBits(src, dest); err != nil { + if err := utils.CopyFileBits(src, dest); err != nil { return fmt.Errorf("Failed copying %q to system manifest repo: %w", src, err) } if _, err = w.Add(fName); err != nil { @@ -342,7 +343,7 @@ func (mos *Mos) UpdateManifest(manifest *SysManifest, newmanifest *SysManifest, } src := filepath.Join(newdir, "manifest.json") dest := filepath.Join(mPath, "manifest.json") - if err := CopyFileBits(src, dest); err != nil { + if err := utils.CopyFileBits(src, dest); err != nil { return fmt.Errorf("Failed copying manifest to final directory") } if _, err = w.Add("manifest.json"); err != nil { diff --git a/pkg/mosconfig/mkboot.go b/pkg/mosconfig/mkboot.go index 9f1b4c6..605134a 100644 --- a/pkg/mosconfig/mkboot.go +++ b/pkg/mosconfig/mkboot.go @@ -10,6 +10,7 @@ import ( "github.com/apex/log" "github.com/pkg/errors" + "github.com/project-machine/mos/pkg/utils" "golang.org/x/sys/unix" "stackerbuild.io/stacker/pkg/lib" ) @@ -89,12 +90,12 @@ type OciBoot struct { } func (o *OciBoot) getBootKit() error { - trustDir, err := MosKeyPath() + trustDir, err := utils.MosKeyPath() if err != nil { return err } keysetPath := filepath.Join(trustDir, o.KeySet) - if !PathExists(keysetPath) { + if !utils.PathExists(keysetPath) { return fmt.Errorf("Keyset not found: %s", o.KeySet) } @@ -115,7 +116,7 @@ func (o *OciBoot) PopulateEFI(mode BootMode, cmdline string, destd string) error if mode == EFIAuto { mode = EFIKernel - if PathExists(filepath.Join(o.BootKit, "shim.efi")) { + if utils.PathExists(filepath.Join(o.BootKit, "shim.efi")) { mode = EFIShim } } @@ -248,14 +249,14 @@ func genESP(fname string, baseDir string) error { return fmt.Errorf("Failed to close file %s", fname) } - if err := RunCommand("mkfs.fat", "-s", "1", "-F", "32", "-n", "EFIBOOT", fname); err != nil { + if err := utils.RunCommand("mkfs.fat", "-s", "1", "-F", "32", "-n", "EFIBOOT", fname); err != nil { return fmt.Errorf("mkfs.fat failed: %w", err) } cmd := []string{"env", "MTOOLS_SKIP_CHECK=1", "mcopy", "-s", "-v", "-i", fname, filepath.Join(baseDir, "efi"), "::efi"} log.Debugf("Running: %s", strings.Join(cmd, " ")) - if err := RunCommand(cmd...); err != nil { + if err := utils.RunCommand(cmd...); err != nil { return err } return nil @@ -376,7 +377,7 @@ func (o *OciBoot) Build() error { return fmt.Errorf("Failed to create directory for modules.squashfs: %v", err) } src := filepath.Join(o.BootKit, "kernel", "modules.squashfs") - if !PathExists(src) { + if !utils.PathExists(src) { src = filepath.Join(o.BootKit, "modules.squashfs") } if err := copyFile(src, modSquashDest); err != nil { @@ -399,7 +400,7 @@ func (o *OciBoot) Build() error { // XXX TODO yikes, but will zot still be writing stuff out? src := o.RepoDir + "/" dest := filepath.Join(tmpd, "oci") - if err := RunCommand("rsync", "-va", src, dest+"/"); err != nil { + if err := utils.RunCommand("rsync", "-va", src, dest+"/"); err != nil { return errors.Wrapf(err, "Failed copying zot cache") } } @@ -416,7 +417,7 @@ func (o *OciBoot) Build() error { cmd = append(cmd, tmpd) log.Infof("Executing: %s", strings.Join(cmd, " ")) - if err := RunCommand(cmd...); err != nil { + if err := utils.RunCommand(cmd...); err != nil { return err } @@ -478,7 +479,7 @@ func BuildProvisioner(keysetName, projectName, isofile string) error { repo := fmt.Sprintf("127.0.0.1:%d", zotPort) name := "machine/livecd:1.0.0" - keyPath, err := MosKeyPath() + keyPath, err := utils.MosKeyPath() if err != nil { return errors.Wrapf(err, "Failed finding mos key path") } @@ -537,7 +538,7 @@ func BuildInstaller(keysetName, projectName, isofile string) error { repo := fmt.Sprintf("127.0.0.1:%d", zotPort) name := "machine/livecd:1.0.0" - keyPath, err := MosKeyPath() + keyPath, err := utils.MosKeyPath() if err != nil { return errors.Wrapf(err, "Failed finding mos key path") } diff --git a/pkg/mosconfig/mos.go b/pkg/mosconfig/mos.go index 309b39f..1c81f70 100644 --- a/pkg/mosconfig/mos.go +++ b/pkg/mosconfig/mos.go @@ -9,6 +9,7 @@ import ( "github.com/apex/log" "github.com/pkg/errors" + "github.com/project-machine/mos/pkg/utils" "golang.org/x/sys/unix" ) @@ -260,7 +261,7 @@ func (mos *Mos) startFsOnly(t *Target) error { return err } dest := filepath.Join(mos.opts.RootDir, "/mnt/atom", t.ServiceName) - if err := EnsureDir(dest); err != nil { + if err := utils.EnsureDir(dest); err != nil { return fmt.Errorf("Unable to create directory %s: %w", dest, err) } if err = unix.Mount(src, dest, "", unix.MS_BIND, ""); err != nil { @@ -303,7 +304,7 @@ func (mos *Mos) writeLxcConfig(t *Target) error { if err := os.RemoveAll(lxcconfigDir); err != nil { return fmt.Errorf("Failed removing pre-existing container config for %q: %w", t.ServiceName, err) } - if err := EnsureDir(lxcconfigDir); err != nil { + if err := utils.EnsureDir(lxcconfigDir); err != nil { return fmt.Errorf("Failed creating container config dir: %w", err) } if err := os.Chmod(lxcStateDir, 0755); err != nil { @@ -430,7 +431,7 @@ func (mos *Mos) StopTarget(t *Target) error { unitName := fmt.Sprintf("%s.service", t.ServiceName) switch t.ServiceType { case ContainerService: - out, rc := RunCommandWithRc("systemctl", "stop", unitName) + out, rc := utils.RunCommandWithRc("systemctl", "stop", unitName) outs := string(out) if rc != 0 && !strings.HasSuffix(outs, "not loaded.\n") { return fmt.Errorf("Failed to stop service %s: %s", t.ServiceName, outs) diff --git a/pkg/mosconfig/mount.go b/pkg/mosconfig/mount.go index df83cab..c676b88 100644 --- a/pkg/mosconfig/mount.go +++ b/pkg/mosconfig/mount.go @@ -4,6 +4,7 @@ import ( "path/filepath" "github.com/pkg/errors" + "github.com/project-machine/mos/pkg/utils" ) // getTargetAt: Get the target called @target from the current host manifest, @@ -52,7 +53,7 @@ func (mos *Mos) Mount(target, dest, url string, ro bool) error { dest = filepath.Join(mos.opts.RootDir, "/mnt/mos", target) } - if err := EnsureDir(dest); err != nil { + if err := utils.EnsureDir(dest); err != nil { return errors.Wrapf(err, "Failed creating mountpoint") } diff --git a/pkg/mosconfig/storage.go b/pkg/mosconfig/storage.go index 47fa83f..aef98d9 100644 --- a/pkg/mosconfig/storage.go +++ b/pkg/mosconfig/storage.go @@ -13,6 +13,7 @@ import ( "github.com/apex/log" "github.com/opencontainers/umoci" "github.com/pkg/errors" + "github.com/project-machine/mos/pkg/utils" "golang.org/x/sys/unix" "stackerbuild.io/stacker/pkg/atomfs" "stackerbuild.io/stacker/pkg/lib" @@ -81,7 +82,7 @@ func (a *AtomfsStorage) metadataPath() string { } func (a *AtomfsStorage) Mount(t *Target, mountpoint string) (func(), error) { - if err := EnsureDir(mountpoint); err != nil { + if err := utils.EnsureDir(mountpoint); err != nil { return func() {}, fmt.Errorf("Failed creating mountpoint %q: %w", mountpoint, err) } @@ -199,7 +200,7 @@ func (a *AtomfsStorage) MountedByHash(target *Target) (string, error) { // have their rootfs visible in this mount namespace. let's // look at the specific mountinfo for the container just to be // sure. - out, rc := RunCommandWithRc("lxc-info", "-H", "-n", target.ServiceName, "-s") + out, rc := utils.RunCommandWithRc("lxc-info", "-H", "-n", target.ServiceName, "-s") if rc != 0 { /* if the service didn't previously exist, it's ok for lxc-ls to fail */ return "", nil @@ -207,7 +208,7 @@ func (a *AtomfsStorage) MountedByHash(target *Target) (string, error) { if strings.TrimSpace(string(out)) != "RUNNING" { return "", nil } - out, rc = RunCommandWithRc("lxc-info", "-H", "-n", target.ServiceName, "-p") + out, rc = utils.RunCommandWithRc("lxc-info", "-H", "-n", target.ServiceName, "-p") if rc != 0 { /* if the service didn't previously exist, it's ok for lxc-ls to fail */ return "", nil @@ -225,7 +226,7 @@ func (a *AtomfsStorage) MountedByHash(target *Target) (string, error) { func (a *AtomfsStorage) SetupTarget(t *Target) error { mp := filepath.Join(a.scratchPath, "roots", t.ServiceName) - mounted, err := IsMountpoint(mp) + mounted, err := utils.IsMountpoint(mp) if err != nil { return fmt.Errorf("Failed checking whether %q is mounted: %w", mp, err) } @@ -236,7 +237,7 @@ func (a *AtomfsStorage) SetupTarget(t *Target) error { } } - err = EnsureDir(mp) + err = utils.EnsureDir(mp) if err != nil { return fmt.Errorf("Failed creating mountpoint %q: %w", mp, err) } @@ -269,7 +270,7 @@ func (a *AtomfsStorage) TargetMountdir(t *Target) (string, error) { func (a *AtomfsStorage) TearDownTarget(name string) error { log.Warnf("tearing down %q", name) mp := filepath.Join(a.scratchPath, "roots", name) - mounted, err := IsMountpoint(mp) + mounted, err := utils.IsMountpoint(mp) if err != nil { return fmt.Errorf("Failed checking whether %q is mounted: %w", mp, err) } @@ -346,7 +347,7 @@ func (a *AtomfsStorage) copyRemote(image string, target *Target) error { log.Debugf("Copying (%#v (image %s) to local storage", target, image) tpath := filepath.Join(a.zotPath, "mos") dest := fmt.Sprintf("oci:%s:%s", tpath, target.Digest) - err := EnsureDir(tpath) + err := utils.EnsureDir(tpath) if err != nil { return errors.Wrapf(err, "Failed creating local zot directory %q", tpath) } diff --git a/pkg/mosconfig/uidmap.go b/pkg/mosconfig/uidmap.go index dad38c7..45f631f 100644 --- a/pkg/mosconfig/uidmap.go +++ b/pkg/mosconfig/uidmap.go @@ -5,6 +5,7 @@ import ( "os" "github.com/lxc/lxd/shared/idmap" + "github.com/project-machine/mos/pkg/utils" ) type uidRangeDefaults struct { @@ -125,11 +126,11 @@ func addUidMapping(set idmap.IdmapSet) error { r := fmt.Sprintf("%d-%d", first, last) cmdStr := []string{"usermod", "-v", r, "root"} - if err := RunCommand(cmdStr...); err != nil { + if err := utils.RunCommand(cmdStr...); err != nil { return fmt.Errorf("Error adding subuid allocation: %w", err) } cmdStr = []string{"usermod", "-w", r, "root"} - if err := RunCommand(cmdStr...); err != nil { + if err := utils.RunCommand(cmdStr...); err != nil { return fmt.Errorf("Error adding subgid allocation: %w", err) } } diff --git a/pkg/mosconfig/update.go b/pkg/mosconfig/update.go index 4fda00e..f804861 100644 --- a/pkg/mosconfig/update.go +++ b/pkg/mosconfig/update.go @@ -7,6 +7,7 @@ import ( "path/filepath" "github.com/pkg/errors" + "github.com/project-machine/mos/pkg/utils" ) func (mos *Mos) Update(url string) error { @@ -24,7 +25,7 @@ func (mos *Mos) Update(url string) error { } // TODO - we will drop the git manifest altogether. - shaSum, err := ShaSum(is.FilePath) + shaSum, err := utils.ShaSum(is.FilePath) if err != nil { return fmt.Errorf("Failed calculating shasum: %w", err) } @@ -67,17 +68,17 @@ func (mos *Mos) Update(url string) error { defer os.RemoveAll(tmpdir) dest := filepath.Join(tmpdir, mFile) - if err = CopyFileBits(is.FilePath, dest); err != nil { + if err = utils.CopyFileBits(is.FilePath, dest); err != nil { return fmt.Errorf("Failed copying %q to %q: %w", is.FilePath, dest, err) } dest = filepath.Join(tmpdir, sFile) - if err = CopyFileBits(is.SignPath, dest); err != nil { + if err = utils.CopyFileBits(is.SignPath, dest); err != nil { return fmt.Errorf("Failed copying %q to %q: %w", is.SignPath, dest, err) } dest = filepath.Join(tmpdir, cFile) - if err = CopyFileBits(is.CertPath, dest); err != nil { + if err = utils.CopyFileBits(is.CertPath, dest); err != nil { return fmt.Errorf("Failed copying %q to %q: %w", is.CertPath, dest, err) } diff --git a/pkg/mosconfig/utils.go b/pkg/mosconfig/utils.go index f941bc8..e6d3df6 100644 --- a/pkg/mosconfig/utils.go +++ b/pkg/mosconfig/utils.go @@ -1,191 +1,20 @@ package mosconfig import ( - "bufio" - "bytes" - "crypto/sha256" "fmt" - "io" "io/fs" - "io/ioutil" "net" "os" "os/exec" "path/filepath" "strings" - "sync" "syscall" "time" "github.com/apex/log" - "github.com/jsipprell/keyctl" - "github.com/msoap/byline" "github.com/pkg/errors" ) -func IsMountpoint(path string) (bool, error) { - return IsMountpointOfDevice(path, "") -} - -func IsMountpointOfDevice(path, devicepath string) (bool, error) { - path = strings.TrimSuffix(path, "/") - f, err := os.Open("/proc/self/mounts") - if err != nil { - return false, err - } - defer f.Close() - - scanner := bufio.NewScanner(f) - for scanner.Scan() { - line := scanner.Text() - fields := strings.Fields(line) - if len(fields) <= 1 { - continue - } - if (fields[1] == path || path == "") && (fields[0] == devicepath || devicepath == "") { - return true, nil - } - } - - return false, nil -} - -func PathExists(d string) bool { - _, err := os.Stat(d) - if err != nil && os.IsNotExist(err) { - return false - } - return true -} - -func EnsureDir(dir string) error { - err := os.MkdirAll(dir, 0755) - if err != nil { - return fmt.Errorf("Failed creating directory %q: %w", dir, err) - } - return nil -} - -// If src is a symlink, copies content, not link. -// TODO - copy the permissions. For now it just makes all new files -// 0644 which is what we want anyway. -func CopyFileBits(src, dest string) error { - in, err := os.Open(src) - if err != nil { - return err - } - defer in.Close() - - if err := EnsureDir(filepath.Dir(dest)); err != nil { - return err - } - out, err := os.OpenFile(dest, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) - if err != nil { - return err - } - defer out.Close() - - _, err = io.Copy(out, in) - if err != nil { - return err - } - return out.Close() -} - -func RunCommandEnv(env []string, args ...string) error { - cmd := exec.Command(args[0], args[1:]...) - cmd.Env = env - output, err := cmd.CombinedOutput() - if err != nil { - return errors.Errorf("%s: %s: %s", strings.Join(args, " "), err, string(output)) - } - return nil -} - -func RunCommand(args ...string) error { - cmd := exec.Command(args[0], args[1:]...) - output, err := cmd.CombinedOutput() - if err != nil { - return fmt.Errorf("%s: %s: %s", strings.Join(args, " "), err, string(output)) - } - return nil -} - -func RunCommandWithRc(args ...string) ([]byte, int) { - out, err := exec.Command(args[0], args[1:]...).CombinedOutput() - return out, GetCommandErrorRC(err) -} - -func GetCommandErrorRCDefault(err error, rcError int) int { - if err == nil { - return 0 - } - exitError, ok := err.(*exec.ExitError) - if ok { - if status, ok := exitError.Sys().(syscall.WaitStatus); ok { - return status.ExitStatus() - } - } - log.Debugf("Unavailable return code for %s. returning %d", err, rcError) - return rcError -} - -func GetCommandErrorRC(err error) int { - return GetCommandErrorRCDefault(err, 127) -} - -func LogCommand(args ...string) error { - return LogCommandWithFunc(log.Infof, args...) -} - -func LogCommandWithFunc(logf func(string, ...interface{}), args ...string) error { - cmd := exec.Command(args[0], args[1:]...) - stdoutPipe, err := cmd.StdoutPipe() - if err != nil { - logf("%s-fail | %s", err) - return err - } - cmd.Stderr = cmd.Stdout - err = cmd.Start() - if err != nil { - logf("%s-fail | %s", args[0], err) - return err - } - pid := cmd.Process.Pid - logf("|%d-start| %q", pid, args) - - var wg sync.WaitGroup - wg.Add(1) - go func() { - err := byline.NewReader(stdoutPipe).Each( - func(line []byte) { - logf("|%d-out | %s", pid, line[:len(line)-1]) - }).Discard() - if err != nil { - log.Fatalf("Unexpected %s", err) - } - wg.Done() - }() - - wg.Wait() - err = cmd.Wait() - - logf("|%d-exit | rc=%d", pid, GetCommandErrorRC(err)) - return err -} - -func ShaSum(fpath string) (string, error) { - f, err := os.Open(fpath) - if err != nil { - return "", err - } - h := sha256.New() - if _, err = io.Copy(h, f); err != nil { - return "", err - } - return fmt.Sprintf("%x", h.Sum(nil)), nil -} - // Taken from stacker's squashfs package // Takes /proc/self/uid_map contents as one string // Returns true if this is a uidmap representing the whole host @@ -247,89 +76,6 @@ func dropHashAlg(d string) string { return d } -// UserDataDir returns the user's data directory -func UserDataDir() (string, error) { - p, err := os.UserHomeDir() - if err != nil { - return "", err - } - return filepath.Join(p, ".local", "share"), nil -} - -// ConfPath returns the user's config directory -func ConfPath(cluster string) string { - configDir, err := os.UserConfigDir() - if err != nil { - return "" - } - return filepath.Join(configDir, "machine", cluster, "machine.yaml") -} - -// MosKeyPath returns the mos/trust key path under which all keysets -// are found. -func MosKeyPath() (string, error) { - dataDir, err := UserDataDir() - if err != nil { - return "", err - } - return filepath.Join(dataDir, "machine", "trust", "keys"), nil -} - -func ReadKeyFromUserKeyring(keyName string) (string, error) { - keyring, err := keyctl.UserKeyring() - if err != nil { - return "", errors.Wrapf(err, "Failed opening user keyring for reading") - } - key, err := keyring.Search(keyName) - if err != nil { - return "", errors.Wrapf(err, "Failed searching user keyring for key: %s", keyName) - } - b, err := key.Get() - if err != nil { - return "", errors.Wrapf(err, "Failed reading %s key from user keyring", keyName) - } - return string(b), nil -} - -// Create a tmpfile, write contents to it, close it, return -// the filename. -func WriteTempFile(dir, prefix, contents string) (string, error) { - f, err := ioutil.TempFile(dir, prefix) - if err != nil { - return "", errors.Wrapf(err, "Failed opening a tempfile") - } - name := f.Name() - _, err = f.Write([]byte(contents)) - defer f.Close() - return name, errors.Wrapf(err, "Failed writing contents to tempfile") -} - -func RunCommandWithOutputErrorRc(args ...string) ([]byte, []byte, int) { - cmd := exec.Command(args[0], args[1:]...) - var stdout, stderr bytes.Buffer - cmd.Stdout = &stdout - cmd.Stderr = &stderr - err := cmd.Run() - return stdout.Bytes(), stderr.Bytes(), GetCommandErrorRC(err) -} - -func RunWithStdin(stdinString string, args ...string) error { - cmd := exec.Command(args[0], args[1:]...) - stdin, err := cmd.StdinPipe() - if err != nil { - return errors.Errorf("%s: %s", strings.Join(args, " "), err) - } - go func() { - defer stdin.Close() - io.WriteString(stdin, stdinString) - }() - output, err := cmd.CombinedOutput() - if err != nil { - return errors.Errorf("%s: %s: %s", strings.Join(args, " "), err, string(output)) - } - return nil -} - // pick first unused port >= min func unusedPort(min int) int { port := min diff --git a/pkg/provider/kvm.go b/pkg/provider/kvm.go index 779b878..2924b18 100644 --- a/pkg/provider/kvm.go +++ b/pkg/provider/kvm.go @@ -14,7 +14,7 @@ import ( "github.com/pkg/errors" "github.com/project-machine/machine/pkg/api" "github.com/project-machine/machine/pkg/client" - "github.com/project-machine/mos/pkg/trust" + "github.com/project-machine/mos/pkg/utils" ) // TODO - can we get machine to auto-detect the uefi-code it needs? @@ -54,7 +54,7 @@ type KVMMachine struct { } func NewKVMProvider() (KVMProvider, error) { - if err := trust.RunCommand("machine", "list"); err != nil { + if err := utils.RunCommand("machine", "list"); err != nil { return KVMProvider{}, errors.Wrapf(err, "machined not running?") } return KVMProvider{}, nil @@ -65,7 +65,7 @@ func (p KVMProvider) Type() ProviderType { } func (p KVMProvider) Exists(mname string) bool { - if err := trust.RunCommand("machine", "info", mname); err == nil { + if err := utils.RunCommand("machine", "info", mname); err == nil { return true } return false @@ -84,21 +84,21 @@ func (p KVMProvider) New(mname, keyproject, UUID string) (Machine, error) { UUID: UUID, } - machineBaseDir, err := trust.MachineDir(m.Name) + machineBaseDir, err := utils.MachineDir(m.Name) if err != nil { return m, errors.Wrapf(err, "Failed getting machine dir") } - if err := trust.EnsureDir(machineBaseDir); err != nil { + if err := utils.EnsureDir(machineBaseDir); err != nil { return m, errors.Wrapf(err, "Failed getting machine dir") } // Create hard drive qcowPath := filepath.Join(machineBaseDir, fmt.Sprintf("%s.qcow2", m.Name)) - if err := trust.RunCommand("qemu-img", "create", "-f", "qcow2", qcowPath, "600G"); err != nil { + if err := utils.RunCommand("qemu-img", "create", "-f", "qcow2", qcowPath, "600G"); err != nil { return m, errors.Wrapf(err, "Failed creating disk") } - keysetDir, projDir, err := trust.KeyProjectDir(m.Keyset, m.Project) + keysetDir, projDir, err := utils.KeyProjectDir(m.Keyset, m.Project) if err != nil { return m, errors.Wrapf(err, "Failed finding keyset path") } @@ -115,7 +115,7 @@ func (p KVMProvider) New(mname, keyproject, UUID string) (Machine, error) { uefiVars := filepath.Join(keysetDir, "bootkit", "ovmf-vars.fd") mData := fmt.Sprintf(KVMTemplate, m.Name, m.Name, uefiVars, provisionISO, qcowPath, sudiPath) - _, _, err = trust.RunWithStdall(mData, "machine", "init", m.Name) + _, _, err = utils.RunWithStdall(mData, "machine", "init", m.Name) if err != nil { return m, errors.Wrapf(err, "Failed initializing machine") } @@ -126,7 +126,7 @@ func (p KVMProvider) New(mname, keyproject, UUID string) (Machine, error) { } func (p KVMProvider) Delete(mname string) error { - if err := trust.RunCommand("machine", "delete", mname); err != nil { + if err := utils.RunCommand("machine", "delete", mname); err != nil { return errors.Wrapf(err, "Failed deleting %q", mname) } return nil @@ -195,7 +195,7 @@ func (m KVMMachine) updateForInstall() error { newDisks := []api.QemuDisk{} for _, d := range machine.Config.Disks { if strings.HasSuffix(d.File, "sudi.vfat") { - _, projDir, err := trust.KeyProjectDir(m.Keyset, m.Project) + _, projDir, err := utils.KeyProjectDir(m.Keyset, m.Project) if err != nil { return errors.Wrapf(err, "Failed finding keyset path") } @@ -311,14 +311,14 @@ func (m KVMMachine) state(desired string) bool { } func (m KVMMachine) Start() error { - if err := trust.RunCommand("machine", "start", m.Name); err != nil { + if err := utils.RunCommand("machine", "start", m.Name); err != nil { return errors.Wrapf(err, "Failed starting %q", m.Name) } return nil } func (m KVMMachine) Stop() error { - if err := trust.RunCommand("machine", "stop", m.Name); err != nil { + if err := utils.RunCommand("machine", "stop", m.Name); err != nil { return errors.Wrapf(err, "Failed stopping %q", m.Name) } return nil diff --git a/pkg/trust/artifacts.go b/pkg/trust/artifacts.go index c065b62..f95b4d1 100644 --- a/pkg/trust/artifacts.go +++ b/pkg/trust/artifacts.go @@ -19,6 +19,7 @@ import ( "github.com/pkg/errors" "github.com/project-machine/bootkit/pkg/cert" "github.com/project-machine/bootkit/pkg/shim" + "github.com/project-machine/mos/pkg/utils" "github.com/project-stacker/stacker/container/idmap" "github.com/project-stacker/stacker/lib" stackeroci "github.com/project-stacker/stacker/oci" @@ -31,7 +32,7 @@ func extractSingleSquash(squashFile string, extractDir string) error { } cmd := []string{"unsquashfs", "-f", "-d", extractDir, squashFile} - return RunCommand(cmd...) + return utils.RunCommand(cmd...) } func unpackSquashLayer(ociDir string, oci casext.Engine, tag string, dest string) error { @@ -153,7 +154,7 @@ func UpdateShim(inShim, newShim, keysetPath string) error { "--key", filepath.Join(keysetPath, "uefi-db", "privkey.pem"), "--cert", filepath.Join(keysetPath, "uefi-db", "cert.pem"), "--output", newShim, inShim} - err = RunCommand(cmd...) + err = utils.RunCommand(cmd...) if err != nil { return errors.Wrapf(err, "failed re-signing shim") } @@ -200,7 +201,7 @@ func SetupBootkit(keysetName, bootkitVersion, mosctlPath string) error { } ociDir := filepath.Join(home, ".cache", "machine", "trust", "bootkit", "oci") bootkitLayer := "bootkit:" + bootkitVersion + "-squashfs" - EnsureDir(ociDir) + utils.EnsureDir(ociDir) cachedOci := fmt.Sprintf("oci:%s:%s", ociDir, bootkitLayer) err = lib.ImageCopy(lib.ImageCopyOpts{ Src: fmt.Sprintf("docker://zothub.io/machine/bootkit/%s", bootkitLayer), @@ -227,20 +228,20 @@ func SetupBootkit(keysetName, bootkitVersion, mosctlPath string) error { os.Rename(filepath.Join(bDir, "bootkit"), bDir+".tmp") os.RemoveAll(bDir) os.Rename(bDir+".tmp", bDir) - mosKeyPath, err := getMosKeyPath() + mosKeyPath, err := utils.GetMosKeyPath() if err != nil { return errors.Wrapf(err, "Failed getting mos keypath") } keysetPath := filepath.Join(mosKeyPath, keysetName) destDir := filepath.Join(keysetPath, "bootkit") - if err := EnsureDir(destDir); err != nil { + if err := utils.EnsureDir(destDir); err != nil { return errors.Wrapf(err, "Failed creating directory %q", destDir) } unchanged := []string{"kernel/modules.squashfs", "ovmf/ovmf-code.fd"} for _, f := range unchanged { - if err := CopyFile(filepath.Join(bDir, f), filepath.Join(destDir, f)); err != nil { + if err := utils.CopyFile(filepath.Join(bDir, f), filepath.Join(destDir, f)); err != nil { return errors.Wrapf(err, "Failed copying %s into new bootkit from %s -> %s", f, bDir, destDir) } } @@ -260,7 +261,7 @@ func SetupBootkit(keysetName, bootkitVersion, mosctlPath string) error { "--cert", filepath.Join(keysetPath, "uki-production", "cert.pem"), "--output", filepath.Join(destDir, "kernel.efi"), newKernel} - err = RunCommand(cmd...) + err = utils.RunCommand(cmd...) if err != nil { return errors.Wrapf(err, "failed re-signing shim") } @@ -268,7 +269,7 @@ func SetupBootkit(keysetName, bootkitVersion, mosctlPath string) error { // Create a bootkit/oci layer with the kernel and shim, for use // in mosctl install tmpoci := filepath.Join(tmpdir, "ocibuild") - if err := EnsureDir(tmpoci); err != nil { + if err := utils.EnsureDir(tmpoci); err != nil { return err } yamlString := fmt.Sprintf(bootkitOciTemplate, keysetPath, keysetPath) @@ -282,12 +283,12 @@ func SetupBootkit(keysetName, bootkitVersion, mosctlPath string) error { "--oci-dir", filepath.Join(tmpoci, "oci"), "--roots-dir", filepath.Join(tmpoci, "roots"), "build", "-f", yamlFile, "--layer-type", "squashfs"} - if err := RunCommand(cmd...); err != nil { + if err := utils.RunCommand(cmd...); err != nil { return errors.Wrapf(err, "Failed building bootkit OCI layer") } src := filepath.Join(tmpoci, "oci") dest := filepath.Join(destDir, "oci") - if err := CopyFiles(src, dest); err != nil { + if err := utils.CopyFiles(src, dest); err != nil { return errors.Wrapf(err, "Failed copying %q to %q", src, dest) } @@ -319,7 +320,7 @@ func SetupBootkit(keysetName, bootkitVersion, mosctlPath string) error { "--add-kek", kekGuid, filepath.Join(keysetPath, "uefi-kek", "cert.pem"), "--add-db", dbGuid, filepath.Join(keysetPath, "uefi-db", "cert.pem"), } - if err := RunCommand(cmd...); err != nil { + if err := utils.RunCommand(cmd...); err != nil { return errors.Wrapf(err, "Failed creating new ovmf vars") } @@ -355,7 +356,7 @@ func extractObj(objdump []string, dir string, piece string) error { } objPath := filepath.Join(dir, "kernel.efi") // Yes we could do this all without shelling out... - err := RunCommand("dd", "if="+objPath, "of="+outName, + err := utils.RunCommand("dd", "if="+objPath, "of="+outName, fmt.Sprintf("skip=%d", offset), fmt.Sprintf("count=%d", size), "iflag=skip_bytes,count_bytes") @@ -391,12 +392,12 @@ func ReplaceManifestCert(dir, keysetPath, customMostctl string) (string, error) newCert := filepath.Join(keysetPath, "manifest-ca", "cert.pem") pcr7Dir := filepath.Join(keysetPath, "pcr7data") - if !PathExists(pcr7Dir) { + if !utils.PathExists(pcr7Dir) { return "", fmt.Errorf("No pcr7data found") } pcr7Cpio := pcr7Dir + ".cpio" - if !PathExists(pcr7Cpio) { - if err := NewCpio(pcr7Cpio, pcr7Dir); err != nil { + if !utils.PathExists(pcr7Cpio) { + if err := utils.NewCpio(pcr7Cpio, pcr7Dir); err != nil { return "", errors.Wrapf(err, "Failed creating pcr7 cpio for %s", filepath.Base(keysetPath)) } } @@ -412,11 +413,11 @@ func ReplaceManifestCert(dir, keysetPath, customMostctl string) (string, error) defer os.RemoveAll(emptydir) manifestCA := filepath.Join(emptydir, "manifestCA.pem") - if err := CopyFile(newCert, manifestCA); err != nil { + if err := utils.CopyFile(newCert, manifestCA); err != nil { return "", errors.Wrapf(err, "Failed copying manifest into empty dir") } - if err := NewCpio(certCpio, manifestCA); err != nil { + if err := utils.NewCpio(certCpio, manifestCA); err != nil { return "", errors.Wrapf(err, "Failed creating cpio archive of manifest cert") } @@ -431,13 +432,13 @@ func ReplaceManifestCert(dir, keysetPath, customMostctl string) (string, error) } if customMostctl != "" { mosctlDir := filepath.Join(emptydir, "/usr/bin") - EnsureDir(mosctlDir) + utils.EnsureDir(mosctlDir) mosctlFile := filepath.Join(mosctlDir, "mosctl") - if err := CopyFile(customMostctl, mosctlFile); err != nil { + if err := utils.CopyFile(customMostctl, mosctlFile); err != nil { return "", errors.Wrapf(err, "Failed copying custom mosctl") } mosCpio := filepath.Join(dir, "mosctl.cpio") - if err := NewCpio(mosCpio, filepath.Join(emptydir, "usr")); err != nil { + if err := utils.NewCpio(mosCpio, filepath.Join(emptydir, "usr")); err != nil { return "", errors.Wrapf(err, "Failed creating mosctl cpio") } files = append(files, mosCpio) @@ -446,7 +447,7 @@ func ReplaceManifestCert(dir, keysetPath, customMostctl string) (string, error) for _, f := range files { if strings.HasSuffix(f, ".gz") { - if err := RunCommand("gunzip", f); err != nil { + if err := utils.RunCommand("gunzip", f); err != nil { return "", errors.Wrapf(err, "Failed gunzipping %s", f) } } @@ -456,7 +457,7 @@ func ReplaceManifestCert(dir, keysetPath, customMostctl string) (string, error) } } - if err := RunCommand("gzip", initrd); err != nil { + if err := utils.RunCommand("gzip", initrd); err != nil { return "", errors.Wrapf(err, "Failed re-zipping initrd.gz") } @@ -477,7 +478,7 @@ func ReplaceManifestCert(dir, keysetPath, customMostctl string) (string, error) filepath.Join(dir, "stubby/stubby.efi"), kret, } - if err := RunCommand(cmd...); err != nil { + if err := utils.RunCommand(cmd...); err != nil { return "", errors.Wrapf(err, "Failed creating kernel.efi") } diff --git a/pkg/trust/computepcr7.go b/pkg/trust/computepcr7.go index 854e26e..f2c595e 100644 --- a/pkg/trust/computepcr7.go +++ b/pkg/trust/computepcr7.go @@ -13,6 +13,7 @@ import ( "github.com/canonical/go-efilib" "github.com/canonical/tcglog-parser" + "github.com/project-machine/mos/pkg/utils" ) const ShimLockGUID = "605dab50-e046-4300-abb6-3dd810dd8b23" @@ -213,13 +214,13 @@ func ComputePCR7(keysetName string) ([]byte, []byte, []byte, error) { // The value will be the resulting pcr7 value when the cert is extended. var ukiKeys = map[string][]byte{"production": nil, "limited": nil, "tpm": nil} - moskeysetPath, err := getMosKeyPath() + moskeysetPath, err := utils.GetMosKeyPath() if err != nil { return nil, nil, nil, err } keysetPath := filepath.Join(moskeysetPath, keysetName) - if !PathExists(keysetPath) { + if !utils.PathExists(keysetPath) { return nil, nil, nil, fmt.Errorf("Unknown keyset '%s', cannot find keyset at path: %q", keysetName, keysetPath) } diff --git a/pkg/trust/run.go b/pkg/trust/run.go index 7321094..cdab682 100644 --- a/pkg/trust/run.go +++ b/pkg/trust/run.go @@ -1,13 +1,9 @@ package trust import ( - "bytes" "fmt" - "io" - "os" "os/exec" "strings" - "syscall" "github.com/apex/log" ) @@ -22,81 +18,3 @@ func run(args ...string) error { log.Infof("(OK) %s: %s", strings.Join(args, " "), string(output)) return nil } - -func GetCommandErrorRCDefault(err error, rcError int) int { - if err == nil { - return 0 - } - exitError, ok := err.(*exec.ExitError) - if ok { - if status, ok := exitError.Sys().(syscall.WaitStatus); ok { - return status.ExitStatus() - } - } - log.Debugf("Unavailable return code for %s. returning %d", err, rcError) - return rcError -} - -func GetCommandErrorRC(err error) int { - return GetCommandErrorRCDefault(err, 127) -} - -func RunCommandWithRc(args ...string) ([]byte, int) { - out, err := exec.Command(args[0], args[1:]...).CombinedOutput() - return out, GetCommandErrorRC(err) -} - -func runEnv(args []string, env []string) error { - log.Debugf("Running: %s (environ %s\n", args, env) - cmd := exec.Command(args[0], args[1:]...) - cmd.Env = append(os.Environ(), env...) - output, err := cmd.CombinedOutput() - if err != nil { - return fmt.Errorf("%s: %s: %s", strings.Join(args, " "), err, string(output)) - } - log.Infof("(OK) %s: %s", strings.Join(args, " "), string(output)) - return nil -} - -// runCapture - execute args and return stdout, stderr and return code. -func runCapture(args ...string) ([]byte, []byte, int) { - cmd := exec.Command(args[0], args[1:]...) //nolint:gosec - var stdout, stderr bytes.Buffer - cmd.Stdout = &stdout - cmd.Stderr = &stderr - cmd.Run() - - log.Debugf("running: %s", strings.Join(args, " ")) - return stdout.Bytes(), stderr.Bytes(), cmd.ProcessState.ExitCode() -} - -// runCaptureStdin - execute args write to stdin then return stdout, stderr and return code. -func runCaptureStdin(stdinString string, args ...string) ([]byte, []byte, int) { - cmd := exec.Command(args[0], args[1:]...) //nolint:gosec - stdin, err := cmd.StdinPipe() - if err != nil { - log.Errorf("Failed constructing stdin '%s' for %v", stdinString, args) - return []byte{}, []byte{}, -1 - } - - go func() { - defer stdin.Close() - io.WriteString(stdin, stdinString) - }() - - var stdout, stderr bytes.Buffer - cmd.Stdout = &stdout - cmd.Stderr = &stderr - cmd.Run() - - return stdout.Bytes(), stderr.Bytes(), cmd.ProcessState.ExitCode() -} - -func RunCommandWithOutputErrorRc(args ...string) ([]byte, []byte, int) { - cmd := exec.Command(args[0], args[1:]...) - var stdout, stderr bytes.Buffer - cmd.Stdout = &stdout - cmd.Stderr = &stderr - err := cmd.Run() - return stdout.Bytes(), stderr.Bytes(), GetCommandErrorRC(err) -} diff --git a/pkg/trust/tpm2-tools.go b/pkg/trust/tpm2-tools.go index 4c59129..3358c61 100644 --- a/pkg/trust/tpm2-tools.go +++ b/pkg/trust/tpm2-tools.go @@ -11,6 +11,7 @@ import ( "strings" "github.com/apex/log" + "github.com/project-machine/mos/pkg/utils" ) // This is the tpm2 backend using tpm2-tools. @@ -41,7 +42,7 @@ func readHostPcr7() ([]byte, error) { defer os.Remove(name) cmd := []string{"tpm2_pcrread", "sha256:7", "-o", name} env := []string{"TPM2TOOLS_TCTI=device:/dev/tpm0"} - err = runEnv(cmd, env) + err = utils.RunCommandEnv(cmd, env) if err != nil { return []byte{}, err } @@ -52,7 +53,7 @@ func readHostPcr7() ([]byte, error) { func curPcr7() (string, error) { var c []byte var err error - if PathExists(RootfsPCRFile) { + if utils.PathExists(RootfsPCRFile) { c, err = ioutil.ReadFile(RootfsPCRFile) } else { c, err = readHostPcr7() @@ -110,7 +111,7 @@ func (c *tpm2V3Context) Tpm2LoadExternal(pubkeyPath string) error { func Tpm2NVIndexLength(nvindex NVIndex) (int, error) { log.Debugf("Tpm2NVIndexLength(nvindex=%s)\n", nvindex.String()) - stdout, stderr, rc := runCapture("tpm2_nvreadpublic", nvindex.String()) + stdout, stderr, rc := utils.RunCommandWithOutputErrorRc("tpm2_nvreadpublic", nvindex.String()) if rc != 0 { return 0, fmt.Errorf("Reading index %s failed:\nstderr: %s\nstdout: %s\n", nvindex, stderr, stdout) } @@ -209,7 +210,7 @@ func (c *tpm2V3Context) Tpm2Read(nvindex NVIndex, size int) (string, error) { cmd = append(cmd, "--auth=session:"+c.sessionFile) } - stdout, stderr, rc := runCapture(cmd...) + stdout, stderr, rc := utils.RunCommandWithOutputErrorRc(cmd...) if rc != 0 { return "", fmt.Errorf("Reading %d bytes at index %s failed:\nstderr: %s\nstdout: %s\n", size, nvindex, stderr, stdout) @@ -219,7 +220,7 @@ func (c *tpm2V3Context) Tpm2Read(nvindex NVIndex, size int) (string, error) { func (c *tpm2V3Context) Tpm2NVWriteAsAdmin(nvindex NVIndex, towrite string) error { cmd := []string{"tpm2_nvwrite", optHierarchyOwner, "--auth=" + c.adminPwd, optInputStdin, nvindex.String()} - stdout, stderr, rc := runCaptureStdin(towrite, cmd...) + stdout, stderr, rc := utils.RunWithStdinRC(towrite, cmd...) if rc != 0 { return fmt.Errorf("Failed running %s [%d]\nError: %s\nOutput: %s\n", cmd, rc, stderr, stdout) } @@ -274,7 +275,7 @@ func (c *tpm2V3Context) Tpm2NVWriteWithPolicy(nvindex NVIndex, towrite string) e optInputStdin, nvindex.String(), } - stdout, stderr, rc := runCaptureStdin(towrite, cmd...) + stdout, stderr, rc := utils.RunWithStdinRC(towrite, cmd...) if rc != 0 { return fmt.Errorf("Failed running %s [%d]\nError: %s\nOutput: %s\n", cmd, rc, stderr, stdout) } @@ -288,7 +289,7 @@ func (c *tpm2V3Context) Tpm2PolicyNV(towrite string) (string, error) { f.Close() cmd := []string{"tpm2_policynv", "--session=" + c.sessionFile, optInputStdin, TPM2IndexEAVersion.String(), "eq", "--policy=" + fname} - stdout, stderr, rc := runCaptureStdin(towrite, cmd...) + stdout, stderr, rc := utils.RunWithStdinRC(towrite, cmd...) if rc != 0 { return "", fmt.Errorf("Failed running %s [%d]\nError: %s\nOutput: %s\n", cmd, rc, stderr, stdout) } @@ -323,7 +324,7 @@ func (c *tpm2V3Context) StorePublic(idx NVIndex, value string) error { } func getTpmBufsize() (int, error) { - out, rc := RunCommandWithRc("tpm2_getcap", "properties-fixed") + out, rc := utils.RunCommandWithRc("tpm2_getcap", "properties-fixed") if rc != 0 { return 0, fmt.Errorf("error %d", rc) } @@ -380,7 +381,7 @@ func (c *tpm2V3Context) Tpm2PolicyAuthorizeTicket(policyDigest, ticketFile strin func Tpm2Read(nvindex NVIndex, size int) (string, error) { log.Debugf("Tpm2Read(nvindex=%s size=%d)\n", nvindex.String(), size) - stdout, stderr, rc := RunCommandWithOutputErrorRc("tpm2_nvread", "-s", fmt.Sprintf("%d", size), nvindex.String()) + stdout, stderr, rc := utils.RunCommandWithOutputErrorRc("tpm2_nvread", "-s", fmt.Sprintf("%d", size), nvindex.String()) if rc != 0 { return "", fmt.Errorf("Reading %d bytes at index %s failed:\nstderr: %s\nstdout: %s\n", size, nvindex, stderr, stdout) @@ -411,7 +412,7 @@ func (c *tpm2V3Context) Tpm2ReadSession(nvindex NVIndex, offset int, size int) ( nvindex.String(), } - stdout, stderr, rc := RunCommandWithOutputErrorRc(cmd...) + stdout, stderr, rc := utils.RunCommandWithOutputErrorRc(cmd...) if rc != 0 { return "", fmt.Errorf("Reading %d bytes at index %s failed:\nstderr: %s\nstdout: %s\n", size, nvindex, stderr, stdout) diff --git a/pkg/trust/tpm2.go b/pkg/trust/tpm2.go index 3e962fe..6e52576 100644 --- a/pkg/trust/tpm2.go +++ b/pkg/trust/tpm2.go @@ -20,6 +20,7 @@ import ( "github.com/anuvu/disko/linux" "github.com/apex/log" "github.com/jsipprell/keyctl" + "github.com/project-machine/mos/pkg/utils" "github.com/urfave/cli" ) @@ -71,7 +72,7 @@ func pathForPartition(p *DiskPart) string { func (t *tpm2V3Context) mountPlaintextPartition() error { // If /pcr7data exists, use it. Otherwise, look for a disk partition // with PBFPartitionTypeID and mount it - if PathExists(SignDataDir) { + if utils.PathExists(SignDataDir) { return nil } @@ -207,7 +208,7 @@ func (t *tpm2V3Context) findDisks() error { return nil } - if !PathExists("/factory") { + if !utils.PathExists("/factory") { dest := "/factory/pbf" err = os.MkdirAll(dest, 0755) if err != nil { @@ -264,7 +265,7 @@ func setupFactory() (string, error) { // can't move-mount out of a MS_SHARED parent, which / is, // so create a MS_SLAVE parent directory. priv := "/priv" - err := EnsureDir(priv) + err := utils.EnsureDir(priv) if err != nil { return "", fmt.Errorf("failed creating directory %s: %w", priv, err) } @@ -278,7 +279,7 @@ func setupFactory() (string, error) { } tmpfsDir := filepath.Join(priv, "factory") - if err := EnsureDir(tmpfsDir); err != nil { + if err := utils.EnsureDir(tmpfsDir); err != nil { return "", fmt.Errorf("Failed creating %q: %w", tmpfsDir, err) } @@ -288,16 +289,16 @@ func setupFactory() (string, error) { dest := filepath.Join(tmpfsDir, "secure") - if err = MountTmpfs(tmpfsDir, "1G"); err != nil { + if err = utils.MountTmpfs(tmpfsDir, "1G"); err != nil { return "", fmt.Errorf("Failed creating tmpfs for certs: %w", err) } if err = os.Chmod(tmpfsDir, 0644); err != nil { return "", fmt.Errorf("Failed making tmpfs private: %w", err) } - if PathExists(PBFMountpoint) { + if utils.PathExists(PBFMountpoint) { privpbf := filepath.Join(priv, PBFMountpoint) - err = EnsureDir(privpbf) + err = utils.EnsureDir(privpbf) if err != nil { log.Warnf("Failed creating %s", privpbf) } @@ -446,13 +447,13 @@ func (t *tpm2V3Context) PartitionForTPM(disk string, luksPassphrase string, wipe } ppartPath := pathForPartition(&pbf) - if stdout, stderr, rc := runCapture("mkfs.ext4", ppartPath); rc != 0 { + if stdout, stderr, rc := utils.RunCommandWithOutputErrorRc("mkfs.ext4", ppartPath); rc != 0 { return fmt.Errorf("Failed to mkfs.ext4 %s [%d]:\n out: %s\n err: %s\n", ppartPath, rc, stdout, stderr) } dest := "/factory/secure" - err = EnsureDir(dest) + err = utils.EnsureDir(dest) if err != nil { return err } @@ -462,7 +463,7 @@ func (t *tpm2V3Context) PartitionForTPM(disk string, luksPassphrase string, wipe } dest = filepath.Join(dest, SignDataDir) - err = CopyFiles(SignDataDir, dest) + err = utils.CopyFiles(SignDataDir, dest) if err != nil { return fmt.Errorf("Failed saving pcr7data onto new PBF: %w", err) } @@ -495,12 +496,12 @@ func (t *tpm2V3Context) PartitionForTPM(disk string, luksPassphrase string, wipe return err } - if stdout, stderr, rc := runCapture("mkfs.ext4", mpath); rc != 0 { + if stdout, stderr, rc := utils.RunCommandWithOutputErrorRc("mkfs.ext4", mpath); rc != 0 { return fmt.Errorf("Failed to mkfs.ext4 %s [%d]:\n out: %s\n err: %s\n", mpath, rc, stdout, stderr) } - if stdout, stderr, rc := runCapture("cryptsetup", "close", name); rc != 0 { + if stdout, stderr, rc := utils.RunCommandWithOutputErrorRc("cryptsetup", "close", name); rc != 0 { return fmt.Errorf("Failed to close luks device: %s [%d]:\n out: %s\n err: %s\n", name, rc, stdout, stderr) } @@ -655,7 +656,9 @@ func (t *tpm2V3Context) InitrdSetup() error { defer func() { if err := t.ExtendPCR7(); err != nil { log.Warnf("Failed extending PCR 7: %v", err) - run("poweroff") + if err2 := run("poweroff"); err2 != nil { + log.Warnf("poweroff command failed: %v", err2) + } log.Fatalf("Failed powering off") } log.Infof("Extended PCR 7") @@ -717,7 +720,7 @@ func (t *tpm2V3Context) InitrdSetup() error { return fmt.Errorf("Key unlink failed: %w", err) } - err = CopyFile("/manifestCA.pem", filepath.Join(dest, "manifestCA.pem")) + err = utils.CopyFile("/manifestCA.pem", filepath.Join(dest, "manifestCA.pem")) if err != nil { return fmt.Errorf("Failed copying the manifest CA parent: %w", err) } @@ -725,15 +728,15 @@ func (t *tpm2V3Context) InitrdSetup() error { // But we also need to access this file during initrd, while // it's still under /priv. We could handle this several ways, // but let's just copy it to /factory/secure/ as well. - err = EnsureDir("/factory/secure") + err = utils.EnsureDir("/factory/secure") if err != nil { return fmt.Errorf("Failed creating /factory/secure in initrd: %w", err) } - err = CopyFile("/priv/factory/secure/server.crt", "/factory/secure/server.crt") + err = utils.CopyFile("/priv/factory/secure/server.crt", "/factory/secure/server.crt") if err != nil { return fmt.Errorf("Failed copying the server certificate: %w", err) } - err = CopyFile("/manifestCA.pem", "/factory/secure/manifestCA.pem") + err = utils.CopyFile("/manifestCA.pem", "/factory/secure/manifestCA.pem") if err != nil { log.Warnf("Failed copying manifest CA parent: %w", err) } @@ -749,7 +752,9 @@ func (t *tpm2V3Context) PreInstall() error { defer func() { if err := t.ExtendPCR7(); err != nil { log.Warnf("Failed extending PCR 7: %v", err) - run("poweroff") + if err2 := run("poweroff"); err2 != nil { + log.Warnf("poweroff command failed: %v", err2) + } log.Fatalf("Failed powering off") } log.Infof("Extended PCR 7") diff --git a/pkg/trust/utils.go b/pkg/trust/utils.go index aae9f0e..5bbb53d 100644 --- a/pkg/trust/utils.go +++ b/pkg/trust/utils.go @@ -1,85 +1,13 @@ package trust import ( - "bytes" "encoding/hex" "fmt" "io" - "os" "os/exec" - "path/filepath" "strings" - "syscall" - - "github.com/pkg/errors" - "github.com/plus3it/gorecurcopy" ) -func EnsureDir(dir string) error { - err := os.MkdirAll(dir, 0755) - if err != nil { - return fmt.Errorf("couldn't make dirs: %w", err) - } - return nil -} - -func PathExists(d string) bool { - _, err := os.Stat(d) - if err != nil && os.IsNotExist(err) { - return false - } - return true -} - -func CopyFile(src, dest string) error { - fstat, err := os.Lstat(src) - if err != nil { - return fmt.Errorf("Error opening %s to copy: %w", src, err) - } - - if (fstat.Mode() & os.ModeSymlink) == os.ModeSymlink { - // TODO - should we? - return fmt.Errorf("Refusing to copy symlink") - } - - if err := EnsureDir(filepath.Dir(dest)); err != nil { - return err - } - - in, err := os.Open(src) - if err != nil { - return err - } - defer in.Close() - - out, err := os.OpenFile(dest, os.O_RDWR|os.O_CREATE|os.O_TRUNC, fstat.Mode().Perm()) - if err != nil { - return err - } - defer out.Close() - - _, err = io.Copy(out, in) - if err != nil { - return err - } - - // TODO - copy xattrs? - - return out.Close() -} - -func MountTmpfs(dest, size string) error { - if err := EnsureDir(dest); err != nil { - return fmt.Errorf("Failed making mount point: %w", err) - } - flags := uintptr(syscall.MS_NODEV | syscall.MS_NOSUID | syscall.MS_NOEXEC) - err := syscall.Mount("tmpfs", dest, "tmpfs", flags, "size="+size) - if err != nil { - return fmt.Errorf("Failed mounting tmpfs onto %s: %w", dest, err) - } - return nil -} - func genPassphrase(nchars int) (string, error) { // each random byte will give us two characters. We prefix with // trust-. So if we want 39 or 40 characters, request (39-6)/2+1 = 17 @@ -120,105 +48,3 @@ func luksOpen(path, key, plaintextPath string) error { func luksFormatLuks2(path, key string) error { return runWithStdin(key, "cryptsetup", "luksFormat", "--type=luks2", "--key-file=-", path) } - -// if src == /tmp/a and dst == /tmp/b, and /tmp/a/x exists, then make -// sure we have /tmp/b/x. -// The way gorecurcopy.CopyDirectory() works, if $dest does not exists, it -// will fail, so create it first. -func CopyFiles(src, dest string) error { - if !PathExists(src) { - return fmt.Errorf("No such directory: %s", src) - } - if err := EnsureDir(dest); err != nil { - return err - } - return gorecurcopy.CopyDirectory(src, dest) -} - -// Run the command @args, passing @stdinString on standard input. Return -// the stdout, stderr, and any error returned. -func RunWithStdall(stdinString string, args ...string) (string, string, error) { - cmd := exec.Command(args[0], args[1:]...) - stdin, err := cmd.StdinPipe() - if err != nil { - return "", "", fmt.Errorf("Failed getting stdin pipe %v: %w", args, err) - } - var stdout, stderr bytes.Buffer - cmd.Stdout = &stdout - cmd.Stderr = &stderr - go func() { - defer stdin.Close() - io.WriteString(stdin, stdinString) - }() - err = cmd.Run() - return stdout.String(), stderr.String(), err -} - -// Run: run a command. Return the output and an error if any. -func Run(args ...string) (string, error) { - cmd := exec.Command(args[0], args[1:]...) - output, err := cmd.CombinedOutput() - if err != nil { - return string(output), errors.Wrapf(err, "Failed running %v", args) - } - return string(output), nil -} - -func RunCommand(args ...string) error { - _, err := Run(args...) - if err != nil { - return err - } - return nil -} - -// UserDataDir returns the user's data directory -func MachineDir(mname string) (string, error) { - p, err := os.UserHomeDir() - if err != nil { - return "", err - } - return filepath.Join(p, ".local", "state", "machine", "machines", mname, mname), nil -} - -// UserDataDir returns the user's data directory -func UserDataDir() (string, error) { - p, err := os.UserHomeDir() - if err != nil { - return "", err - } - return filepath.Join(p, ".local", "share"), nil -} - -// Get the location where keysets are stored -func getMosKeyPath() (string, error) { - dataDir, err := UserDataDir() - if err != nil { - return "", err - } - return filepath.Join(dataDir, "machine", "trust", "keys"), nil -} - -func KeyProjectDir(keyset, project string) (string, string, error) { - d, err := getMosKeyPath() - if err != nil { - return "", "", err - } - k := filepath.Join(d, keyset) - p := filepath.Join(k, "manifest", project) - return k, p, nil -} - -// Just create a cpio file. @path will be the top level directory -// or the file in the new cpio file index. -func NewCpio(cpio, path string) error { - parent := filepath.Dir(path) - target := filepath.Base(path) - - bashcmd := "cd " + parent + "; find " + target + "| cpio --create --owner=+0:+0 -H newc --quiet > " + cpio - if err := RunCommand("/bin/bash", "-c", bashcmd); err != nil { - return errors.Wrapf(err, "Failed creating cpio of %s -> %s", path, cpio) - } - - return nil -} diff --git a/pkg/utils/run.go b/pkg/utils/run.go new file mode 100644 index 0000000..c918bc7 --- /dev/null +++ b/pkg/utils/run.go @@ -0,0 +1,176 @@ +package utils + +import ( + "bytes" + "fmt" + "io" + "os" + "os/exec" + "strings" + "sync" + "syscall" + + "github.com/apex/log" + "github.com/msoap/byline" + "github.com/pkg/errors" +) + +// Run the command @args, passing @stdinString on standard input. Return +// the stdout, stderr, and any error returned. +func RunWithStdall(stdinString string, args ...string) (string, string, error) { + cmd := exec.Command(args[0], args[1:]...) + stdin, err := cmd.StdinPipe() + if err != nil { + return "", "", fmt.Errorf("Failed getting stdin pipe %v: %w", args, err) + } + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + go func() { + defer stdin.Close() + io.WriteString(stdin, stdinString) + }() + err = cmd.Run() + return stdout.String(), stderr.String(), err +} + +// Run: run a command. Return the output and an error if any. +func Run(args ...string) (string, error) { + cmd := exec.Command(args[0], args[1:]...) + output, err := cmd.CombinedOutput() + if err != nil { + return string(output), errors.Wrapf(err, "Failed running %v", args) + } + return string(output), nil +} + +func RunCommandEnv(args []string, env []string) error { + log.Debugf("Running: %s (environ %s\n", args, env) + cmd := exec.Command(args[0], args[1:]...) + cmd.Env = append(os.Environ(), env...) + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("%s: %s: %s", strings.Join(args, " "), err, string(output)) + } + log.Infof("(OK) %s: %s", strings.Join(args, " "), string(output)) + return nil +} + +func RunCommand(args ...string) error { + _, err := Run(args...) + if err != nil { + return err + } + return nil +} + +func RunCommandWithRc(args ...string) ([]byte, int) { + out, err := exec.Command(args[0], args[1:]...).CombinedOutput() + return out, GetCommandErrorRC(err) +} + +func GetCommandErrorRCDefault(err error, rcError int) int { + if err == nil { + return 0 + } + exitError, ok := err.(*exec.ExitError) + if ok { + if status, ok := exitError.Sys().(syscall.WaitStatus); ok { + return status.ExitStatus() + } + } + log.Debugf("Unavailable return code for %s. returning %d", err, rcError) + return rcError +} + +func GetCommandErrorRC(err error) int { + return GetCommandErrorRCDefault(err, 127) +} + +func LogCommand(args ...string) error { + return LogCommandWithFunc(log.Infof, args...) +} + +func LogCommandWithFunc(logf func(string, ...interface{}), args ...string) error { + cmd := exec.Command(args[0], args[1:]...) + stdoutPipe, err := cmd.StdoutPipe() + if err != nil { + logf("%s-fail | %s", err) + return err + } + cmd.Stderr = cmd.Stdout + err = cmd.Start() + if err != nil { + logf("%s-fail | %s", args[0], err) + return err + } + pid := cmd.Process.Pid + logf("|%d-start| %q", pid, args) + + var wg sync.WaitGroup + wg.Add(1) + go func() { + err := byline.NewReader(stdoutPipe).Each( + func(line []byte) { + logf("|%d-out | %s", pid, line[:len(line)-1]) + }).Discard() + if err != nil { + log.Fatalf("Unexpected %s", err) + } + wg.Done() + }() + + wg.Wait() + err = cmd.Wait() + + logf("|%d-exit | rc=%d", pid, GetCommandErrorRC(err)) + return err +} + +func RunCommandWithOutputErrorRc(args ...string) ([]byte, []byte, int) { + cmd := exec.Command(args[0], args[1:]...) + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + err := cmd.Run() + return stdout.Bytes(), stderr.Bytes(), GetCommandErrorRC(err) +} + +// RunWithStdinRC - execute args write to stdin then return stdout, stderr and return code. +func RunWithStdinRC(stdinString string, args ...string) ([]byte, []byte, int) { + cmd := exec.Command(args[0], args[1:]...) //nolint:gosec + stdin, err := cmd.StdinPipe() + if err != nil { + log.Errorf("Failed constructing stdin '%s' for %v", stdinString, args) + return []byte{}, []byte{}, -1 + } + + go func() { + defer stdin.Close() + io.WriteString(stdin, stdinString) + }() + + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + cmd.Run() + + return stdout.Bytes(), stderr.Bytes(), cmd.ProcessState.ExitCode() +} + +func RunWithStdin(stdinString string, args ...string) error { + cmd := exec.Command(args[0], args[1:]...) + stdin, err := cmd.StdinPipe() + if err != nil { + return errors.Errorf("%s: %s", strings.Join(args, " "), err) + } + go func() { + defer stdin.Close() + io.WriteString(stdin, stdinString) + }() + output, err := cmd.CombinedOutput() + if err != nil { + return errors.Errorf("%s: %s: %s", strings.Join(args, " "), err, string(output)) + } + return nil +} diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go new file mode 100644 index 0000000..7acfd40 --- /dev/null +++ b/pkg/utils/utils.go @@ -0,0 +1,255 @@ +package utils + +import ( + "bufio" + "crypto/sha256" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "syscall" + + "github.com/jsipprell/keyctl" + "github.com/pkg/errors" + "github.com/plus3it/gorecurcopy" +) + +func EnsureDir(dir string) error { + err := os.MkdirAll(dir, 0755) + if err != nil { + return fmt.Errorf("couldn't make dirs: %w", err) + } + return nil +} + +func IsMountpoint(path string) (bool, error) { + return IsMountpointOfDevice(path, "") +} + +func IsMountpointOfDevice(path, devicepath string) (bool, error) { + path = strings.TrimSuffix(path, "/") + f, err := os.Open("/proc/self/mounts") + if err != nil { + return false, err + } + defer f.Close() + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + line := scanner.Text() + fields := strings.Fields(line) + if len(fields) <= 1 { + continue + } + if (fields[1] == path || path == "") && (fields[0] == devicepath || devicepath == "") { + return true, nil + } + } + + return false, nil +} + +func PathExists(d string) bool { + _, err := os.Stat(d) + if err != nil && os.IsNotExist(err) { + return false + } + return true +} + +// If src is a symlink, copies content, not link. +// TODO - copy the permissions. For now it just makes all new files +// 0644 which is what we want anyway. +func CopyFileBits(src, dest string) error { + in, err := os.Open(src) + if err != nil { + return err + } + defer in.Close() + + if err := EnsureDir(filepath.Dir(dest)); err != nil { + return err + } + out, err := os.OpenFile(dest, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + return err + } + defer out.Close() + + _, err = io.Copy(out, in) + if err != nil { + return err + } + return out.Close() +} + +func CopyFile(src, dest string) error { + fstat, err := os.Lstat(src) + if err != nil { + return fmt.Errorf("Error opening %s to copy: %w", src, err) + } + + if (fstat.Mode() & os.ModeSymlink) == os.ModeSymlink { + // TODO - should we? + return fmt.Errorf("Refusing to copy symlink") + } + + if err := EnsureDir(filepath.Dir(dest)); err != nil { + return err + } + + in, err := os.Open(src) + if err != nil { + return err + } + defer in.Close() + + out, err := os.OpenFile(dest, os.O_RDWR|os.O_CREATE|os.O_TRUNC, fstat.Mode().Perm()) + if err != nil { + return err + } + defer out.Close() + + _, err = io.Copy(out, in) + if err != nil { + return err + } + + // TODO - copy xattrs? + + return out.Close() +} + +// if src == /tmp/a and dst == /tmp/b, and /tmp/a/x exists, then make +// sure we have /tmp/b/x. +// The way gorecurcopy.CopyDirectory() works, if $dest does not exists, it +// will fail, so create it first. +func CopyFiles(src, dest string) error { + if !PathExists(src) { + return fmt.Errorf("No such directory: %s", src) + } + if err := EnsureDir(dest); err != nil { + return err + } + return gorecurcopy.CopyDirectory(src, dest) +} + +func MountTmpfs(dest, size string) error { + if err := EnsureDir(dest); err != nil { + return fmt.Errorf("Failed making mount point: %w", err) + } + flags := uintptr(syscall.MS_NODEV | syscall.MS_NOSUID | syscall.MS_NOEXEC) + err := syscall.Mount("tmpfs", dest, "tmpfs", flags, "size="+size) + if err != nil { + return fmt.Errorf("Failed mounting tmpfs onto %s: %w", dest, err) + } + return nil +} + +// Create a tmpfile, write contents to it, close it, return +// the filename. +func WriteTempFile(dir, prefix, contents string) (string, error) { + f, err := ioutil.TempFile(dir, prefix) + if err != nil { + return "", errors.Wrapf(err, "Failed opening a tempfile") + } + name := f.Name() + _, err = f.Write([]byte(contents)) + defer f.Close() + return name, errors.Wrapf(err, "Failed writing contents to tempfile") +} + +func ShaSum(fpath string) (string, error) { + f, err := os.Open(fpath) + if err != nil { + return "", err + } + h := sha256.New() + if _, err = io.Copy(h, f); err != nil { + return "", err + } + return fmt.Sprintf("%x", h.Sum(nil)), nil +} + +func MachineDir(mname string) (string, error) { + p, err := os.UserHomeDir() + if err != nil { + return "", err + } + return filepath.Join(p, ".local", "state", "machine", "machines", mname, mname), nil +} + +// UserDataDir returns the user's data directory +func UserDataDir() (string, error) { + p, err := os.UserHomeDir() + if err != nil { + return "", err + } + return filepath.Join(p, ".local", "share"), nil +} + +// Get the location where keysets are stored +func GetMosKeyPath() (string, error) { + dataDir, err := UserDataDir() + if err != nil { + return "", err + } + return filepath.Join(dataDir, "machine", "trust", "keys"), nil +} + +// MosKeyPath returns the mos/trust key path under which all keysets +// are found. +func MosKeyPath() (string, error) { + return GetMosKeyPath() +} + +// ConfPath returns the user's config directory +func ConfPath(cluster string) string { + configDir, err := os.UserConfigDir() + if err != nil { + return "" + } + return filepath.Join(configDir, "machine", cluster, "machine.yaml") +} + +func ReadKeyFromUserKeyring(keyName string) (string, error) { + keyring, err := keyctl.UserKeyring() + if err != nil { + return "", errors.Wrapf(err, "Failed opening user keyring for reading") + } + key, err := keyring.Search(keyName) + if err != nil { + return "", errors.Wrapf(err, "Failed searching user keyring for key: %s", keyName) + } + b, err := key.Get() + if err != nil { + return "", errors.Wrapf(err, "Failed reading %s key from user keyring", keyName) + } + return string(b), nil +} + +func KeyProjectDir(keyset, project string) (string, string, error) { + d, err := GetMosKeyPath() + if err != nil { + return "", "", err + } + k := filepath.Join(d, keyset) + p := filepath.Join(k, "manifest", project) + return k, p, nil +} + +// Just create a cpio file. @path will be the top level directory +// or the file in the new cpio file index. +func NewCpio(cpio, path string) error { + parent := filepath.Dir(path) + target := filepath.Base(path) + + bashcmd := "cd " + parent + "; find " + target + "| cpio --create --owner=+0:+0 -H newc --quiet > " + cpio + if err := RunCommand("/bin/bash", "-c", bashcmd); err != nil { + return errors.Wrapf(err, "Failed creating cpio of %s -> %s", path, cpio) + } + + return nil +}