func (f *dockerFetcher) fetch(u *url.URL) (*os.File, error) { tmpDir, err := f.getTmpDir() if err != nil { return nil, err } defer os.RemoveAll(tmpDir) registryURL := strings.TrimPrefix(u.String(), "docker://") user, password := f.getCreds(registryURL) // for now, always use https:// when fetching docker images acis, err := docker2aci.Convert(registryURL, true /* squash */, tmpDir, tmpDir, user, password, false /* insecure */) if err != nil { return nil, fmt.Errorf("error converting docker image to ACI: %v", err) } aciFile, err := os.Open(acis[0]) if err != nil { return nil, fmt.Errorf("error opening squashed ACI file: %v", err) } return aciFile, nil }
// fetch opens/downloads and verifies the remote ACI. // If appName is not "", it will be used to check that the manifest contain the correct appName // If ascFile is not nil, it will be used as the signature file and ascURL will be ignored. // If Keystore is nil signature verification will be skipped, regardless of ascFile. // fetch returns the signer, an *os.File representing the ACI, and an error if any. // err will be nil if the ACI fetches successfully and the ACI is verified. func (f *fetcher) fetch(appName string, aciURL, ascURL string, ascFile *os.File, etag string) (*openpgp.Entity, *os.File, *cacheData, error) { var ( entity *openpgp.Entity cd *cacheData ) u, err := url.Parse(aciURL) if err != nil { return nil, nil, nil, fmt.Errorf("error parsing ACI url: %v", err) } if u.Scheme == "docker" { registryURL := strings.TrimPrefix(aciURL, "docker://") tmpDir, err := f.s.TmpDir() if err != nil { return nil, nil, nil, fmt.Errorf("error creating temporary dir for docker to ACI conversion: %v", err) } indexName := docker2aci.GetIndexName(registryURL) user := "" password := "" if creds, ok := f.dockerAuth[indexName]; ok { user = creds.User password = creds.Password } acis, err := docker2aci.Convert(registryURL, true, tmpDir, tmpDir, user, password) if err != nil { return nil, nil, nil, fmt.Errorf("error converting docker image to ACI: %v", err) } aciFile, err := os.Open(acis[0]) if err != nil { return nil, nil, nil, fmt.Errorf("error opening squashed ACI file: %v", err) } return nil, aciFile, nil, nil } var retrySignature bool if f.ks != nil && ascFile == nil { u, err := url.Parse(ascURL) if err != nil { return nil, nil, nil, fmt.Errorf("error parsing ASC url: %v", err) } if u.Scheme == "file" { ascFile, err = os.Open(u.Path) if err != nil { return nil, nil, nil, fmt.Errorf("error opening signature file: %v", err) } } else { stderr("Downloading signature from %v\n", ascURL) ascFile, err = f.s.TmpFile() if err != nil { return nil, nil, nil, fmt.Errorf("error setting up temporary file: %v", err) } defer os.Remove(ascFile.Name()) err = f.downloadSignatureFile(ascURL, ascFile) switch err { case errStatusAccepted: retrySignature = true stderr("rkt: server requested deferring the signature download") case nil: break default: return nil, nil, nil, fmt.Errorf("error downloading the signature file: %v", err) } } defer ascFile.Close() } // check if the identity used by the signature is in the store before a // possibly expensive download. This is only an optimization and it's // ok to skip the test if the signature will be downloaded later. if !retrySignature && f.ks != nil && appName != "" { if _, err := ascFile.Seek(0, 0); err != nil { return nil, nil, nil, fmt.Errorf("error seeking signature file: %v", err) } if entity, err = f.ks.CheckSignature(appName, bytes.NewReader([]byte{}), ascFile); err != nil { if _, ok := err.(pgperrors.SignatureError); !ok { return nil, nil, nil, err } } } var aciFile *os.File if u.Scheme == "file" { aciFile, err = os.Open(u.Path) if err != nil { return nil, nil, nil, fmt.Errorf("error opening ACI file: %v", err) } } else { aciFile, err = f.s.TmpFile() if err != nil { return nil, aciFile, nil, fmt.Errorf("error setting up temporary file: %v", err) } defer os.Remove(aciFile.Name()) if cd, err = f.downloadACI(aciURL, aciFile, etag); err != nil { return nil, nil, nil, fmt.Errorf("error downloading ACI: %v", err) } if cd.useCached { return nil, nil, cd, nil } } if retrySignature { if err = f.downloadSignatureFile(ascURL, ascFile); err != nil { return nil, aciFile, nil, fmt.Errorf("error downloading the signature file: %v", err) } } manifest, err := aci.ManifestFromImage(aciFile) if err != nil { return nil, aciFile, nil, err } // Check if the downloaded ACI has the correct app name. // The check is only performed when the aci is downloaded through the // discovery protocol, but not with local files or full URL. if appName != "" && manifest.Name.String() != appName { return nil, aciFile, nil, fmt.Errorf("error when reading the app name: %q expected but %q found", appName, manifest.Name.String()) } if f.ks != nil { if _, err := aciFile.Seek(0, 0); err != nil { return nil, aciFile, nil, fmt.Errorf("error seeking ACI file: %v", err) } if _, err := ascFile.Seek(0, 0); err != nil { return nil, aciFile, nil, fmt.Errorf("error seeking signature file: %v", err) } if entity, err = f.ks.CheckSignature(manifest.Name.String(), aciFile, ascFile); err != nil { return nil, aciFile, nil, err } } if _, err := aciFile.Seek(0, 0); err != nil { return nil, aciFile, nil, fmt.Errorf("error seeking ACI file: %v", err) } return entity, aciFile, cd, nil }
// fetch opens/downloads and verifies the remote ACI. // If appName is not "", it will be used to check that the manifest contain the correct appName // If ascFile is not nil, it will be used as the signature file and ascURL will be ignored. // If Keystore is nil signature verification will be skipped, regardless of ascFile. // fetch returns the signer, an *os.File representing the ACI, and an error if any. // err will be nil if the ACI fetches successfully and the ACI is verified. func (f *fetcher) fetch(appName string, aciURL, ascURL string, ascFile *os.File, etag string) (*openpgp.Entity, *os.File, *cacheData, error) { var ( entity *openpgp.Entity cd *cacheData ) u, err := url.Parse(aciURL) if err != nil { return nil, nil, nil, fmt.Errorf("error parsing ACI url: %v", err) } if u.Scheme == "docker" { registryURL := strings.TrimPrefix(aciURL, "docker://") storeTmpDir, err := f.s.TmpDir() if err != nil { return nil, nil, nil, fmt.Errorf("error creating temporary dir for docker to ACI conversion: %v", err) } tmpDir, err := ioutil.TempDir(storeTmpDir, "docker2aci-") if err != nil { return nil, nil, nil, err } defer os.RemoveAll(tmpDir) indexName := docker2aci.GetIndexName(registryURL) user := "" password := "" if creds, ok := f.dockerAuth[indexName]; ok { user = creds.User password = creds.Password } acis, err := docker2aci.Convert(registryURL, true, tmpDir, tmpDir, user, password) if err != nil { return nil, nil, nil, fmt.Errorf("error converting docker image to ACI: %v", err) } aciFile, err := os.Open(acis[0]) if err != nil { return nil, nil, nil, fmt.Errorf("error opening squashed ACI file: %v", err) } return nil, aciFile, nil, nil } // attempt to automatically fetch the public key in case it is available on a TLS connection. if globalFlags.TrustKeysFromHttps && !globalFlags.InsecureFlags.SkipTlsCheck() && appName != "" && f.ks != nil { m := &pubkey.Manager{ AuthPerHost: f.headers, InsecureAllowHttp: false, TrustKeysFromHttps: true, Ks: f.ks, Debug: globalFlags.Debug, } pkls, err := m.GetPubKeyLocations(appName) if err != nil { stderr("Error determining key location: %v", err) } else { if err := m.AddKeys(pkls, appName, pubkey.AcceptForce, pubkey.OverrideDeny); err != nil { stderr("Error adding keys: %v", err) } } } var retrySignature bool if f.ks != nil && ascFile == nil { u, err := url.Parse(ascURL) if err != nil { return nil, nil, nil, fmt.Errorf("error parsing ASC url: %v", err) } if u.Scheme == "file" { ascFile, err = os.Open(u.Path) if err != nil { return nil, nil, nil, fmt.Errorf("error opening signature file: %v", err) } } else { stderr("Downloading signature from %v\n", ascURL) ascFile, err = f.s.TmpFile() if err != nil { return nil, nil, nil, fmt.Errorf("error setting up temporary file: %v", err) } defer os.Remove(ascFile.Name()) err = f.downloadSignatureFile(ascURL, ascFile) switch err { case errStatusAccepted: retrySignature = true stderr("rkt: server requested deferring the signature download") case nil: break default: return nil, nil, nil, fmt.Errorf("error downloading the signature file: %v", err) } } defer ascFile.Close() } // check if the identity used by the signature is in the store before a // possibly expensive download. This is only an optimization and it's // ok to skip the test if the signature will be downloaded later. if !retrySignature && f.ks != nil && appName != "" { if _, err := ascFile.Seek(0, 0); err != nil { return nil, nil, nil, fmt.Errorf("error seeking signature file: %v", err) } if entity, err = f.ks.CheckSignature(appName, bytes.NewReader([]byte{}), ascFile); err != nil { if _, ok := err.(pgperrors.SignatureError); !ok { return nil, nil, nil, err } } } var aciFile *os.File if u.Scheme == "file" { aciFile, err = os.Open(u.Path) if err != nil { return nil, nil, nil, fmt.Errorf("error opening ACI file: %v", err) } } else { h := sha512.New() h.Write([]byte(aciURL)) aciURLHash := f.s.HashToKey(h) aciFile, err = f.s.TmpNamedFile(aciURLHash) if err != nil { return nil, aciFile, nil, fmt.Errorf("error setting up temporary file: %v", err) } defer os.Remove(aciFile.Name()) if cd, err = f.downloadACI(aciURL, aciFile, etag); err != nil { return nil, nil, nil, fmt.Errorf("error downloading ACI: %v", err) } if cd.useCached { return nil, nil, cd, nil } } if retrySignature { if err = f.downloadSignatureFile(ascURL, ascFile); err != nil { return nil, aciFile, nil, fmt.Errorf("error downloading the signature file: %v", err) } } manifest, err := aci.ManifestFromImage(aciFile) if err != nil { return nil, aciFile, nil, fmt.Errorf("invalid image manifest: %v", err) } // Check if the downloaded ACI has the correct app name. // The check is only performed when the aci is downloaded through the // discovery protocol, but not with local files or full URL. if appName != "" && manifest.Name.String() != appName { return nil, aciFile, nil, fmt.Errorf("error when reading the app name: %q expected but %q found", appName, manifest.Name.String()) } if f.ks != nil { if _, err := aciFile.Seek(0, 0); err != nil { return nil, aciFile, nil, fmt.Errorf("error seeking ACI file: %v", err) } if _, err := ascFile.Seek(0, 0); err != nil { return nil, aciFile, nil, fmt.Errorf("error seeking signature file: %v", err) } if entity, err = f.ks.CheckSignature(manifest.Name.String(), aciFile, ascFile); err != nil { return nil, aciFile, nil, err } } if _, err := aciFile.Seek(0, 0); err != nil { return nil, aciFile, nil, fmt.Errorf("error seeking ACI file: %v", err) } return entity, aciFile, cd, nil }
// fetch opens/downloads and verifies the remote ACI. // If ascFile is not nil, it will be used as the signature file and ascURL will be ignored. // If Keystore is nil signature verification will be skipped, regardless of ascFile. // fetch returns the signer, an *os.File representing the ACI, and an error if any. // err will be nil if the ACI fetches successfully and the ACI is verified. func (f *fetcher) fetch(aciURL, ascURL string, ascFile *os.File) (*openpgp.Entity, *os.File, error) { var entity *openpgp.Entity u, err := url.Parse(aciURL) if err != nil { return nil, nil, fmt.Errorf("error parsing ACI url: %v", err) } if u.Scheme == "docker" { registryURL := strings.TrimPrefix(aciURL, "docker://") tmpDir, err := f.s.TmpDir() if err != nil { return nil, nil, fmt.Errorf("error creating temporary dir for docker to ACI conversion: %v", err) } indexName := docker2aci.GetIndexName(registryURL) user := "" password := "" if creds, ok := f.dockerAuth[indexName]; ok { user = creds.User password = creds.Password } acis, err := docker2aci.Convert(registryURL, true, tmpDir, tmpDir, user, password) if err != nil { return nil, nil, fmt.Errorf("error converting docker image to ACI: %v", err) } aciFile, err := os.Open(acis[0]) if err != nil { return nil, nil, fmt.Errorf("error opening squashed ACI file: %v", err) } return nil, aciFile, nil } var retrySignature bool if f.ks != nil && ascFile == nil { u, err := url.Parse(ascURL) if err != nil { return nil, nil, fmt.Errorf("error parsing ASC url: %v", err) } if u.Scheme == "file" { ascFile, err = os.Open(u.Path) if err != nil { return nil, nil, fmt.Errorf("error opening signature file: %v", err) } } else { stderr("Downloading signature from %v\n", ascURL) ascFile, err = f.s.TmpFile() if err != nil { return nil, nil, fmt.Errorf("error setting up temporary file: %v", err) } defer os.Remove(ascFile.Name()) err = f.downloadSignatureFile(ascURL, ascFile) switch err { case errStatusAccepted: retrySignature = true stderr("rkt: server requested deferring the signature download") case nil: break default: return nil, nil, fmt.Errorf("error downloading the signature file: %v", err) } } defer ascFile.Close() } var aciFile *os.File if u.Scheme == "file" { aciFile, err = os.Open(u.Path) if err != nil { return nil, nil, fmt.Errorf("error opening ACI file: %v", err) } } else { aciFile, err = f.s.TmpFile() if err != nil { return nil, aciFile, fmt.Errorf("error setting up temporary file: %v", err) } defer os.Remove(aciFile.Name()) if err = f.downloadACI(aciURL, aciFile); err != nil { return nil, nil, fmt.Errorf("error downloading ACI: %v", err) } } if retrySignature { if err = f.downloadSignatureFile(ascURL, ascFile); err != nil { return nil, aciFile, fmt.Errorf("error downloading the signature file: %v", err) } } if f.ks != nil { manifest, err := aci.ManifestFromImage(aciFile) if err != nil { return nil, aciFile, err } if _, err := aciFile.Seek(0, 0); err != nil { return nil, aciFile, fmt.Errorf("error seeking ACI file: %v", err) } if _, err := ascFile.Seek(0, 0); err != nil { return nil, aciFile, fmt.Errorf("error seeking signature file: %v", err) } if entity, err = f.ks.CheckSignature(manifest.Name.String(), aciFile, ascFile); err != nil { return nil, aciFile, err } } if _, err := aciFile.Seek(0, 0); err != nil { return nil, aciFile, fmt.Errorf("error seeking ACI file: %v", err) } return entity, aciFile, nil }