func (h *Host) TrustKey(prefix types.ACIdentifier, location, fingerprint string) error { if location == "" { if prefix == keystore.Root { return errors.New("Cannot discover root key!") } location = prefix.String() } _, kf, err := fetch.OpenPubKey(location) if err != nil { return errors.Trace(err) } defer kf.Close() path, err := h.Keystore().StoreTrustedKey(prefix, kf, fingerprint) if err != nil { return errors.Trace(err) } if path == "" { h.ui.Println("Key NOT accepted") } else { h.ui.Printf("Key accepted and saved as %v\n", path) } return nil }
func (ks *Keystore) walk(name types.ACIdentifier, fn func(prefix types.ACIdentifier, path string) error) error { var namePath string if !name.Empty() { namePath = ks.prefixPath(name) } return filepath.Walk(ks.Path, func(path string, fi os.FileInfo, err error) error { if err != nil && !os.IsNotExist(err) { return err } if fi == nil { return nil } if fi.IsDir() { if namePath == "" || strings.HasPrefix(namePath, path) || fi.Name() == "@" { return nil } else { return filepath.SkipDir } } if prefix, err := pathToACIdentifier(path); err != nil { return errors.Trace(err) } else { return fn(prefix, path) } }) }
func (h *Host) getLocalImage(hash types.Hash, name types.ACIdentifier, labels types.Labels) (*Image, error) { if hash.Empty() && name.Empty() { return nil, errors.Trace(ErrUsage) } if !hash.Empty() { if idStr, err := os.Readlink(h.Path("images", hash.String())); os.IsNotExist(err) { return nil, ErrNotFound } else if err != nil { return nil, errors.Trace(err) } else if id := uuid.Parse(idStr); id == nil { return nil, errors.Errorf("Invalid UUID: %v", idStr) } else if img, err := LoadImage(h, id); err != nil { return nil, errors.Trace(err) } else { return img, nil } } else if imgs, err := h.Images(); err != nil { return nil, errors.Trace(err) } else { for _, img := range imgs { if img.Manifest.Name != name { continue } if !acutil.MatchLabels(labels, img.Manifest.Labels) { continue } // TODO: multiple matches? return img, nil } return nil, ErrNotFound } }
func (ts *TestStore) GetACI(name types.ACIdentifier, labels types.Labels) (string, error) { for _, aci := range ts.acis { if aci.ImageManifest.Name.String() == name.String() { return aci.key, nil } } return "", fmt.Errorf("aci not found") }
func (ms *conversionStore) GetACI(name types.ACIdentifier, labels types.Labels) (string, error) { for _, aci := range ms.acis { // we implement this function to comply with the interface so don't // bother implementing a proper label check if aci.ImageManifest.Name.String() == name.String() { return aci.key, nil } } return "", fmt.Errorf("aci not found") }
func imageNameToAppName(name types.ACIdentifier) (*types.ACName, error) { parts := strings.Split(name.String(), "/") last := parts[len(parts)-1] sn, err := types.SanitizeACName(last) if err != nil { return nil, err } return types.MustACName(sn), nil }
func doubleCheckImage(img *Image, hash types.Hash, name types.ACIdentifier, labels types.Labels) error { if !hash.Empty() && hash != *img.Hash { return stderrors.New("Image hash mismatch") } if !name.Empty() && name != img.Manifest.Name { return stderrors.New("Image name mismatch") } if !acutil.MatchLabels(labels, img.Manifest.Labels) { return stderrors.New("Image label mismatch") } return nil }
func (h *Host) getImage(hash types.Hash, name types.ACIdentifier, labels types.Labels) (*Image, error) { if img, err := h.getLocalImage(hash, name, labels); err == nil { return img, nil } else if err == ErrNotFound { // TODO: possibility to switch off autodiscovery? if name.Empty() { // Can't (auto)discover anonymous image return nil, err } return h.fetchImage(name, labels) } else { return nil, errors.Trace(err) } }
func (ks *Keystore) StoreTrustedKey(prefix types.ACIdentifier, key *os.File, acceptFingerprint string) (string, error) { if prefix.Empty() { panic("Empty prefix!") } if accepted, err := reviewKey(prefix, key, acceptFingerprint); err != nil { return "", err } else if !accepted { return "", nil } pubkeyBytes, err := ioutil.ReadAll(key) if err != nil { return "", err } dir := ks.prefixPath(prefix) if err := os.MkdirAll(dir, 0750); err != nil { return "", err } entityList, err := openpgp.ReadArmoredKeyRing(bytes.NewReader(pubkeyBytes)) if err != nil { return "", err } // FIXME: cargo cult from rkt // FIXME: can we import more than one key here, and note only one? // Maybe we should split and re-armor the entityList? trustedKeyPath := filepath.Join(dir, fingerprintToFilename(entityList[0].PrimaryKey.Fingerprint)) if err := ioutil.WriteFile(trustedKeyPath, pubkeyBytes, 0640); err != nil { return "", err } return trustedKeyPath, nil }
func (ks *Keystore) prefixPath(prefix types.ACIdentifier) string { if prefix.Empty() { panic("Empty prefix!") } return filepath.Join(ks.Path, strings.Replace(string(prefix), "/", ",", -1)) }
// GetACI retrieves the ACI that best matches the provided app name and labels. // The returned value is the blob store key of the retrieved ACI. // If there are multiple matching ACIs choose the latest one (defined as the // last one imported in the store). // If no version label is requested, ACIs marked as latest in the ACIInfo are // preferred. func (s Store) GetACI(name types.ACIdentifier, labels types.Labels) (string, error) { var curaciinfo *ACIInfo versionRequested := false if _, ok := labels.Get("version"); ok { versionRequested = true } var aciinfos []*ACIInfo err := s.db.Do(func(tx *sql.Tx) error { var err error aciinfos, _, err = GetACIInfosWithAppName(tx, name.String()) return err }) if err != nil { return "", err } nextKey: for _, aciinfo := range aciinfos { im, err := s.GetImageManifest(aciinfo.BlobKey) if err != nil { return "", fmt.Errorf("error getting image manifest: %v", err) } // The image manifest must have all the requested labels for _, l := range labels { ok := false for _, rl := range im.Labels { if l.Name == rl.Name && l.Value == rl.Value { ok = true break } } if !ok { continue nextKey } } if curaciinfo != nil { // If no version is requested prefer the acis marked as latest if !versionRequested { if !curaciinfo.Latest && aciinfo.Latest { curaciinfo = aciinfo continue nextKey } if curaciinfo.Latest && !aciinfo.Latest { continue nextKey } } // If multiple matching image manifests are found, choose the latest imported in the cas. if aciinfo.ImportTime.After(curaciinfo.ImportTime) { curaciinfo = aciinfo } } else { curaciinfo = aciinfo } } if curaciinfo != nil { return curaciinfo.BlobKey, nil } return "", fmt.Errorf("aci not found") }
func (h *Host) ImportImage(name types.ACIdentifier, aci, asc *os.File) (_ *Image, erv error) { newId := uuid.NewRandom() newIdStr := newId.String() ui := ui.NewUI("magenta", "import", newIdStr) if name.Empty() { ui.Println("Starting import") } else { ui.Printf("Starting import of %v", name) } if asc != nil { ui.Debug("Checking signature") didKeyDiscovery := false ks := h.Keystore() checkSig: if ety, err := ks.CheckSignature(name, aci, asc); err == openpgp_err.ErrUnknownIssuer && !didKeyDiscovery { ui.Println("Image signed by an unknown issuer, attempting to discover public key...") if err := h.TrustKey(name, "", ""); err != nil { return nil, errors.Trace(err) } didKeyDiscovery = true aci.Seek(0, os.SEEK_SET) asc.Seek(0, os.SEEK_SET) goto checkSig } else if err != nil { return nil, errors.Trace(err) } else { ui.Println("Valid signature for", name, "by:") ui.Println(keystore.KeyDescription(ety)) // FIXME:ui aci.Seek(0, os.SEEK_SET) asc.Seek(0, os.SEEK_SET) } } else { ui.Debug("No signature to check") } img := NewImage(h, newId) defer func() { if erv != nil { img.Destroy() } }() if err := os.MkdirAll(img.Path(), 0700); err != nil { return nil, errors.Trace(err) } // Save copy of the signature if asc != nil { ui.Debug("Saving signature copy") if ascCopy, err := os.OpenFile(img.Path("aci.asc"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0400); err != nil { return nil, errors.Trace(err) } else { _, err := io.Copy(ascCopy, asc) ascCopy.Close() if err != nil { return nil, errors.Trace(err) } } } // Load manifest ui.Debug("Loading manifest") manifestBytes, err := run.Command("tar", "-xOqf", "-", "manifest").ReadFrom(aci).Output() if err != nil { return nil, errors.Trace(err) } aci.Seek(0, os.SEEK_SET) if err = json.Unmarshal(manifestBytes, &img.Manifest); err != nil { return nil, errors.Trace(err) } if !name.Empty() && name != img.Manifest.Name { return nil, errors.Errorf("ACI name mismatch: downloaded %#v, got %#v instead", name, img.Manifest.Name) } if len(img.Manifest.Dependencies) == 0 { ui.Debug("No dependencies to fetch") if _, err := h.Dataset.CreateDataset(path.Join("images", newIdStr), "-o", "mountpoint="+h.Dataset.Path("images", newIdStr, "rootfs")); err != nil { return nil, errors.Trace(err) } } else { for i, dep := range img.Manifest.Dependencies { ui.Println("Looking for dependency:", dep.ImageName, dep.Labels, dep.ImageID) if dimg, err := h.getImageDependency(dep); err != nil { return nil, errors.Trace(err) } else { // We get a copy of the dependency struct when iterating, not // a pointer to it. We need to write to the slice's index to // save the hash to the real manifest. img.Manifest.Dependencies[i].ImageID = dimg.Hash if i == 0 { ui.Printf("Cloning parent %v as base rootfs\n", dimg) if ds, err := dimg.Clone(path.Join(h.Dataset.Name, "images", newIdStr), h.Dataset.Path("images", newIdStr, "rootfs")); err != nil { return nil, errors.Trace(err) } else { img.rootfs = ds } } else { return nil, errors.New("Not implemented") } } } } if err := img.saveManifest(); err != nil { return nil, errors.Trace(err) } ui.Println("Unpacking rootfs") // Save us a copy of the original, compressed ACI aciCopy, err := os.OpenFile(img.Path("aci"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0400) if err != nil { return nil, errors.Trace(err) } defer aciCopy.Close() aciZRd := io.TeeReader(fetch.ProgressBarFileReader(aci), aciCopy) // Decompress tarball for checksum aciRd, err := DecompressingReader(aciZRd) if err != nil { return nil, errors.Trace(err) } hash := sha512.New() aciRd = io.TeeReader(aciRd, hash) // Unpack the image. We trust system's tar, no need to roll our own untarCmd := run.Command("tar", "-C", img.Path(), "-xf", "-", "rootfs") untar, err := untarCmd.StdinPipe() if err != nil { return nil, errors.Trace(err) } if err := untarCmd.Start(); err != nil { return nil, errors.Trace(err) } // FIXME: defer killing process if survived if _, err := io.Copy(untar, aciRd); err != nil { return nil, errors.Trace(err) } if err := untar.Close(); err != nil { return nil, errors.Trace(err) } if err := untarCmd.Wait(); err != nil { return nil, errors.Trace(err) } if hash, err := types.NewHash(fmt.Sprintf("sha512-%x", hash.Sum(nil))); err != nil { // CAN'T HAPPEN return nil, errors.Trace(err) } else { ui.Println("Successfully imported", hash) img.Hash = hash } // TODO: enforce PathWhiteList if err := img.sealImage(); err != nil { return nil, errors.Trace(err) } return img, nil }