pull_v2.go 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033
  1. package distribution // import "github.com/docker/docker/distribution"
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "io/ioutil"
  8. "net/url"
  9. "os"
  10. "runtime"
  11. "strings"
  12. "github.com/containerd/containerd/log"
  13. "github.com/containerd/containerd/platforms"
  14. "github.com/docker/distribution"
  15. "github.com/docker/distribution/manifest/manifestlist"
  16. "github.com/docker/distribution/manifest/ocischema"
  17. "github.com/docker/distribution/manifest/schema1"
  18. "github.com/docker/distribution/manifest/schema2"
  19. "github.com/docker/distribution/reference"
  20. "github.com/docker/distribution/registry/api/errcode"
  21. "github.com/docker/distribution/registry/client/auth"
  22. "github.com/docker/distribution/registry/client/transport"
  23. "github.com/docker/docker/distribution/metadata"
  24. "github.com/docker/docker/distribution/xfer"
  25. "github.com/docker/docker/image"
  26. v1 "github.com/docker/docker/image/v1"
  27. "github.com/docker/docker/layer"
  28. "github.com/docker/docker/pkg/ioutils"
  29. "github.com/docker/docker/pkg/progress"
  30. "github.com/docker/docker/pkg/stringid"
  31. "github.com/docker/docker/pkg/system"
  32. refstore "github.com/docker/docker/reference"
  33. "github.com/docker/docker/registry"
  34. digest "github.com/opencontainers/go-digest"
  35. specs "github.com/opencontainers/image-spec/specs-go/v1"
  36. "github.com/pkg/errors"
  37. "github.com/sirupsen/logrus"
  38. )
  39. var (
  40. errRootFSMismatch = errors.New("layers from manifest don't match image configuration")
  41. errRootFSInvalid = errors.New("invalid rootfs in image configuration")
  42. )
  43. // ImageConfigPullError is an error pulling the image config blob
  44. // (only applies to schema2).
  45. type ImageConfigPullError struct {
  46. Err error
  47. }
  48. // Error returns the error string for ImageConfigPullError.
  49. func (e ImageConfigPullError) Error() string {
  50. return "error pulling image configuration: " + e.Err.Error()
  51. }
  52. type v2Puller struct {
  53. V2MetadataService metadata.V2MetadataService
  54. endpoint registry.APIEndpoint
  55. config *ImagePullConfig
  56. repoInfo *registry.RepositoryInfo
  57. repo distribution.Repository
  58. // confirmedV2 is set to true if we confirm we're talking to a v2
  59. // registry. This is used to limit fallbacks to the v1 protocol.
  60. confirmedV2 bool
  61. manifestStore *manifestStore
  62. }
  63. func (p *v2Puller) Pull(ctx context.Context, ref reference.Named, platform *specs.Platform) (err error) {
  64. // TODO(tiborvass): was ReceiveTimeout
  65. p.repo, p.confirmedV2, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull")
  66. if err != nil {
  67. logrus.Warnf("Error getting v2 registry: %v", err)
  68. return err
  69. }
  70. p.manifestStore.remote, err = p.repo.Manifests(ctx)
  71. if err != nil {
  72. return err
  73. }
  74. if err = p.pullV2Repository(ctx, ref, platform); err != nil {
  75. if _, ok := err.(fallbackError); ok {
  76. return err
  77. }
  78. if continueOnError(err, p.endpoint.Mirror) {
  79. return fallbackError{
  80. err: err,
  81. confirmedV2: p.confirmedV2,
  82. transportOK: true,
  83. }
  84. }
  85. }
  86. return err
  87. }
  88. func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named, platform *specs.Platform) (err error) {
  89. var layersDownloaded bool
  90. if !reference.IsNameOnly(ref) {
  91. layersDownloaded, err = p.pullV2Tag(ctx, ref, platform)
  92. if err != nil {
  93. return err
  94. }
  95. } else {
  96. tags, err := p.repo.Tags(ctx).All(ctx)
  97. if err != nil {
  98. // If this repository doesn't exist on V2, we should
  99. // permit a fallback to V1.
  100. return allowV1Fallback(err)
  101. }
  102. // The v2 registry knows about this repository, so we will not
  103. // allow fallback to the v1 protocol even if we encounter an
  104. // error later on.
  105. p.confirmedV2 = true
  106. for _, tag := range tags {
  107. tagRef, err := reference.WithTag(ref, tag)
  108. if err != nil {
  109. return err
  110. }
  111. pulledNew, err := p.pullV2Tag(ctx, tagRef, platform)
  112. if err != nil {
  113. // Since this is the pull-all-tags case, don't
  114. // allow an error pulling a particular tag to
  115. // make the whole pull fall back to v1.
  116. if fallbackErr, ok := err.(fallbackError); ok {
  117. return fallbackErr.err
  118. }
  119. return err
  120. }
  121. // pulledNew is true if either new layers were downloaded OR if existing images were newly tagged
  122. // TODO(tiborvass): should we change the name of `layersDownload`? What about message in WriteStatus?
  123. layersDownloaded = layersDownloaded || pulledNew
  124. }
  125. }
  126. writeStatus(reference.FamiliarString(ref), p.config.ProgressOutput, layersDownloaded)
  127. return nil
  128. }
  129. type v2LayerDescriptor struct {
  130. digest digest.Digest
  131. diffID layer.DiffID
  132. repoInfo *registry.RepositoryInfo
  133. repo distribution.Repository
  134. V2MetadataService metadata.V2MetadataService
  135. tmpFile *os.File
  136. verifier digest.Verifier
  137. src distribution.Descriptor
  138. }
  139. func (ld *v2LayerDescriptor) Key() string {
  140. return "v2:" + ld.digest.String()
  141. }
  142. func (ld *v2LayerDescriptor) ID() string {
  143. return stringid.TruncateID(ld.digest.String())
  144. }
  145. func (ld *v2LayerDescriptor) DiffID() (layer.DiffID, error) {
  146. if ld.diffID != "" {
  147. return ld.diffID, nil
  148. }
  149. return ld.V2MetadataService.GetDiffID(ld.digest)
  150. }
  151. func (ld *v2LayerDescriptor) Download(ctx context.Context, progressOutput progress.Output) (io.ReadCloser, int64, error) {
  152. logrus.Debugf("pulling blob %q", ld.digest)
  153. var (
  154. err error
  155. offset int64
  156. )
  157. if ld.tmpFile == nil {
  158. ld.tmpFile, err = createDownloadFile()
  159. if err != nil {
  160. return nil, 0, xfer.DoNotRetry{Err: err}
  161. }
  162. } else {
  163. offset, err = ld.tmpFile.Seek(0, io.SeekEnd)
  164. if err != nil {
  165. logrus.Debugf("error seeking to end of download file: %v", err)
  166. offset = 0
  167. ld.tmpFile.Close()
  168. if err := os.Remove(ld.tmpFile.Name()); err != nil {
  169. logrus.Errorf("Failed to remove temp file: %s", ld.tmpFile.Name())
  170. }
  171. ld.tmpFile, err = createDownloadFile()
  172. if err != nil {
  173. return nil, 0, xfer.DoNotRetry{Err: err}
  174. }
  175. } else if offset != 0 {
  176. logrus.Debugf("attempting to resume download of %q from %d bytes", ld.digest, offset)
  177. }
  178. }
  179. tmpFile := ld.tmpFile
  180. layerDownload, err := ld.open(ctx)
  181. if err != nil {
  182. logrus.Errorf("Error initiating layer download: %v", err)
  183. return nil, 0, retryOnError(err)
  184. }
  185. if offset != 0 {
  186. _, err := layerDownload.Seek(offset, io.SeekStart)
  187. if err != nil {
  188. if err := ld.truncateDownloadFile(); err != nil {
  189. return nil, 0, xfer.DoNotRetry{Err: err}
  190. }
  191. return nil, 0, err
  192. }
  193. }
  194. size, err := layerDownload.Seek(0, io.SeekEnd)
  195. if err != nil {
  196. // Seek failed, perhaps because there was no Content-Length
  197. // header. This shouldn't fail the download, because we can
  198. // still continue without a progress bar.
  199. size = 0
  200. } else {
  201. if size != 0 && offset > size {
  202. logrus.Debug("Partial download is larger than full blob. Starting over")
  203. offset = 0
  204. if err := ld.truncateDownloadFile(); err != nil {
  205. return nil, 0, xfer.DoNotRetry{Err: err}
  206. }
  207. }
  208. // Restore the seek offset either at the beginning of the
  209. // stream, or just after the last byte we have from previous
  210. // attempts.
  211. _, err = layerDownload.Seek(offset, io.SeekStart)
  212. if err != nil {
  213. return nil, 0, err
  214. }
  215. }
  216. reader := progress.NewProgressReader(ioutils.NewCancelReadCloser(ctx, layerDownload), progressOutput, size-offset, ld.ID(), "Downloading")
  217. defer reader.Close()
  218. if ld.verifier == nil {
  219. ld.verifier = ld.digest.Verifier()
  220. }
  221. _, err = io.Copy(tmpFile, io.TeeReader(reader, ld.verifier))
  222. if err != nil {
  223. if err == transport.ErrWrongCodeForByteRange {
  224. if err := ld.truncateDownloadFile(); err != nil {
  225. return nil, 0, xfer.DoNotRetry{Err: err}
  226. }
  227. return nil, 0, err
  228. }
  229. return nil, 0, retryOnError(err)
  230. }
  231. progress.Update(progressOutput, ld.ID(), "Verifying Checksum")
  232. if !ld.verifier.Verified() {
  233. err = fmt.Errorf("filesystem layer verification failed for digest %s", ld.digest)
  234. logrus.Error(err)
  235. // Allow a retry if this digest verification error happened
  236. // after a resumed download.
  237. if offset != 0 {
  238. if err := ld.truncateDownloadFile(); err != nil {
  239. return nil, 0, xfer.DoNotRetry{Err: err}
  240. }
  241. return nil, 0, err
  242. }
  243. return nil, 0, xfer.DoNotRetry{Err: err}
  244. }
  245. progress.Update(progressOutput, ld.ID(), "Download complete")
  246. logrus.Debugf("Downloaded %s to tempfile %s", ld.ID(), tmpFile.Name())
  247. _, err = tmpFile.Seek(0, io.SeekStart)
  248. if err != nil {
  249. tmpFile.Close()
  250. if err := os.Remove(tmpFile.Name()); err != nil {
  251. logrus.Errorf("Failed to remove temp file: %s", tmpFile.Name())
  252. }
  253. ld.tmpFile = nil
  254. ld.verifier = nil
  255. return nil, 0, xfer.DoNotRetry{Err: err}
  256. }
  257. // hand off the temporary file to the download manager, so it will only
  258. // be closed once
  259. ld.tmpFile = nil
  260. return ioutils.NewReadCloserWrapper(tmpFile, func() error {
  261. tmpFile.Close()
  262. err := os.RemoveAll(tmpFile.Name())
  263. if err != nil {
  264. logrus.Errorf("Failed to remove temp file: %s", tmpFile.Name())
  265. }
  266. return err
  267. }), size, nil
  268. }
  269. func (ld *v2LayerDescriptor) Close() {
  270. if ld.tmpFile != nil {
  271. ld.tmpFile.Close()
  272. if err := os.RemoveAll(ld.tmpFile.Name()); err != nil {
  273. logrus.Errorf("Failed to remove temp file: %s", ld.tmpFile.Name())
  274. }
  275. }
  276. }
  277. func (ld *v2LayerDescriptor) truncateDownloadFile() error {
  278. // Need a new hash context since we will be redoing the download
  279. ld.verifier = nil
  280. if _, err := ld.tmpFile.Seek(0, io.SeekStart); err != nil {
  281. logrus.Errorf("error seeking to beginning of download file: %v", err)
  282. return err
  283. }
  284. if err := ld.tmpFile.Truncate(0); err != nil {
  285. logrus.Errorf("error truncating download file: %v", err)
  286. return err
  287. }
  288. return nil
  289. }
  290. func (ld *v2LayerDescriptor) Registered(diffID layer.DiffID) {
  291. // Cache mapping from this layer's DiffID to the blobsum
  292. ld.V2MetadataService.Add(diffID, metadata.V2Metadata{Digest: ld.digest, SourceRepository: ld.repoInfo.Name.Name()})
  293. }
  294. func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, platform *specs.Platform) (tagUpdated bool, err error) {
  295. var (
  296. tagOrDigest string // Used for logging/progress only
  297. dgst digest.Digest
  298. mt string
  299. size int64
  300. )
  301. if digested, isDigested := ref.(reference.Canonical); isDigested {
  302. dgst = digested.Digest()
  303. tagOrDigest = digested.String()
  304. } else if tagged, isTagged := ref.(reference.NamedTagged); isTagged {
  305. tagService := p.repo.Tags(ctx)
  306. desc, err := tagService.Get(ctx, tagged.Tag())
  307. if err != nil {
  308. return false, allowV1Fallback(err)
  309. }
  310. dgst = desc.Digest
  311. tagOrDigest = tagged.Tag()
  312. mt = desc.MediaType
  313. size = desc.Size
  314. } else {
  315. return false, fmt.Errorf("internal error: reference has neither a tag nor a digest: %s", reference.FamiliarString(ref))
  316. }
  317. ctx = log.WithLogger(ctx, logrus.WithFields(
  318. logrus.Fields{
  319. "digest": dgst,
  320. "remote": ref,
  321. }))
  322. manifest, err := p.manifestStore.Get(ctx, specs.Descriptor{
  323. MediaType: mt,
  324. Digest: dgst,
  325. Size: size,
  326. })
  327. if err != nil {
  328. return false, err
  329. }
  330. if manifest == nil {
  331. return false, fmt.Errorf("image manifest does not exist for tag or digest %q", tagOrDigest)
  332. }
  333. if m, ok := manifest.(*schema2.DeserializedManifest); ok {
  334. var allowedMediatype bool
  335. for _, t := range p.config.Schema2Types {
  336. if m.Manifest.Config.MediaType == t {
  337. allowedMediatype = true
  338. break
  339. }
  340. }
  341. if !allowedMediatype {
  342. configClass := mediaTypeClasses[m.Manifest.Config.MediaType]
  343. if configClass == "" {
  344. configClass = "unknown"
  345. }
  346. return false, invalidManifestClassError{m.Manifest.Config.MediaType, configClass}
  347. }
  348. }
  349. // If manSvc.Get succeeded, we can be confident that the registry on
  350. // the other side speaks the v2 protocol.
  351. p.confirmedV2 = true
  352. logrus.Debugf("Pulling ref from V2 registry: %s", reference.FamiliarString(ref))
  353. progress.Message(p.config.ProgressOutput, tagOrDigest, "Pulling from "+reference.FamiliarName(p.repo.Named()))
  354. var (
  355. id digest.Digest
  356. manifestDigest digest.Digest
  357. )
  358. switch v := manifest.(type) {
  359. case *schema1.SignedManifest:
  360. if p.config.RequireSchema2 {
  361. return false, fmt.Errorf("invalid manifest: not schema2")
  362. }
  363. // give registries time to upgrade to schema2 and only warn if we know a registry has been upgraded long time ago
  364. // TODO: condition to be removed
  365. if reference.Domain(ref) == "docker.io" {
  366. msg := fmt.Sprintf("Image %s uses outdated schema1 manifest format. Please upgrade to a schema2 image for better future compatibility. More information at https://docs.docker.com/registry/spec/deprecated-schema-v1/", ref)
  367. logrus.Warn(msg)
  368. progress.Message(p.config.ProgressOutput, "", msg)
  369. }
  370. id, manifestDigest, err = p.pullSchema1(ctx, ref, v, platform)
  371. if err != nil {
  372. return false, err
  373. }
  374. case *schema2.DeserializedManifest:
  375. id, manifestDigest, err = p.pullSchema2(ctx, ref, v, platform)
  376. if err != nil {
  377. return false, err
  378. }
  379. case *ocischema.DeserializedManifest:
  380. id, manifestDigest, err = p.pullOCI(ctx, ref, v, platform)
  381. if err != nil {
  382. return false, err
  383. }
  384. case *manifestlist.DeserializedManifestList:
  385. id, manifestDigest, err = p.pullManifestList(ctx, ref, v, platform)
  386. if err != nil {
  387. return false, err
  388. }
  389. default:
  390. return false, invalidManifestFormatError{}
  391. }
  392. progress.Message(p.config.ProgressOutput, "", "Digest: "+manifestDigest.String())
  393. if p.config.ReferenceStore != nil {
  394. oldTagID, err := p.config.ReferenceStore.Get(ref)
  395. if err == nil {
  396. if oldTagID == id {
  397. return false, addDigestReference(p.config.ReferenceStore, ref, manifestDigest, id)
  398. }
  399. } else if err != refstore.ErrDoesNotExist {
  400. return false, err
  401. }
  402. if canonical, ok := ref.(reference.Canonical); ok {
  403. if err = p.config.ReferenceStore.AddDigest(canonical, id, true); err != nil {
  404. return false, err
  405. }
  406. } else {
  407. if err = addDigestReference(p.config.ReferenceStore, ref, manifestDigest, id); err != nil {
  408. return false, err
  409. }
  410. if err = p.config.ReferenceStore.AddTag(ref, id, true); err != nil {
  411. return false, err
  412. }
  413. }
  414. }
  415. return true, nil
  416. }
  417. func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unverifiedManifest *schema1.SignedManifest, platform *specs.Platform) (id digest.Digest, manifestDigest digest.Digest, err error) {
  418. var verifiedManifest *schema1.Manifest
  419. verifiedManifest, err = verifySchema1Manifest(unverifiedManifest, ref)
  420. if err != nil {
  421. return "", "", err
  422. }
  423. rootFS := image.NewRootFS()
  424. // remove duplicate layers and check parent chain validity
  425. err = fixManifestLayers(verifiedManifest)
  426. if err != nil {
  427. return "", "", err
  428. }
  429. var descriptors []xfer.DownloadDescriptor
  430. // Image history converted to the new format
  431. var history []image.History
  432. // Note that the order of this loop is in the direction of bottom-most
  433. // to top-most, so that the downloads slice gets ordered correctly.
  434. for i := len(verifiedManifest.FSLayers) - 1; i >= 0; i-- {
  435. blobSum := verifiedManifest.FSLayers[i].BlobSum
  436. var throwAway struct {
  437. ThrowAway bool `json:"throwaway,omitempty"`
  438. }
  439. if err := json.Unmarshal([]byte(verifiedManifest.History[i].V1Compatibility), &throwAway); err != nil {
  440. return "", "", err
  441. }
  442. h, err := v1.HistoryFromConfig([]byte(verifiedManifest.History[i].V1Compatibility), throwAway.ThrowAway)
  443. if err != nil {
  444. return "", "", err
  445. }
  446. history = append(history, h)
  447. if throwAway.ThrowAway {
  448. continue
  449. }
  450. layerDescriptor := &v2LayerDescriptor{
  451. digest: blobSum,
  452. repoInfo: p.repoInfo,
  453. repo: p.repo,
  454. V2MetadataService: p.V2MetadataService,
  455. }
  456. descriptors = append(descriptors, layerDescriptor)
  457. }
  458. // The v1 manifest itself doesn't directly contain an OS. However,
  459. // the history does, but unfortunately that's a string, so search through
  460. // all the history until hopefully we find one which indicates the OS.
  461. // supertest2014/nyan is an example of a registry image with schemav1.
  462. configOS := runtime.GOOS
  463. if system.LCOWSupported() {
  464. type config struct {
  465. Os string `json:"os,omitempty"`
  466. }
  467. for _, v := range verifiedManifest.History {
  468. var c config
  469. if err := json.Unmarshal([]byte(v.V1Compatibility), &c); err == nil {
  470. if c.Os != "" {
  471. configOS = c.Os
  472. break
  473. }
  474. }
  475. }
  476. }
  477. // In the situation that the API call didn't specify an OS explicitly, but
  478. // we support the operating system, switch to that operating system.
  479. // eg FROM supertest2014/nyan with no platform specifier, and docker build
  480. // with no --platform= flag under LCOW.
  481. requestedOS := ""
  482. if platform != nil {
  483. requestedOS = platform.OS
  484. } else if system.IsOSSupported(configOS) {
  485. requestedOS = configOS
  486. }
  487. // Early bath if the requested OS doesn't match that of the configuration.
  488. // This avoids doing the download, only to potentially fail later.
  489. if !strings.EqualFold(configOS, requestedOS) {
  490. return "", "", fmt.Errorf("cannot download image with operating system %q when requesting %q", configOS, requestedOS)
  491. }
  492. resultRootFS, release, err := p.config.DownloadManager.Download(ctx, *rootFS, configOS, descriptors, p.config.ProgressOutput)
  493. if err != nil {
  494. return "", "", err
  495. }
  496. defer release()
  497. config, err := v1.MakeConfigFromV1Config([]byte(verifiedManifest.History[0].V1Compatibility), &resultRootFS, history)
  498. if err != nil {
  499. return "", "", err
  500. }
  501. imageID, err := p.config.ImageStore.Put(ctx, config)
  502. if err != nil {
  503. return "", "", err
  504. }
  505. manifestDigest = digest.FromBytes(unverifiedManifest.Canonical)
  506. return imageID, manifestDigest, nil
  507. }
  508. func (p *v2Puller) pullSchema2Layers(ctx context.Context, target distribution.Descriptor, layers []distribution.Descriptor, platform *specs.Platform) (id digest.Digest, err error) {
  509. if _, err := p.config.ImageStore.Get(ctx, target.Digest); err == nil {
  510. // If the image already exists locally, no need to pull
  511. // anything.
  512. return target.Digest, nil
  513. }
  514. var descriptors []xfer.DownloadDescriptor
  515. // Note that the order of this loop is in the direction of bottom-most
  516. // to top-most, so that the downloads slice gets ordered correctly.
  517. for _, d := range layers {
  518. layerDescriptor := &v2LayerDescriptor{
  519. digest: d.Digest,
  520. repo: p.repo,
  521. repoInfo: p.repoInfo,
  522. V2MetadataService: p.V2MetadataService,
  523. src: d,
  524. }
  525. descriptors = append(descriptors, layerDescriptor)
  526. }
  527. configChan := make(chan []byte, 1)
  528. configErrChan := make(chan error, 1)
  529. layerErrChan := make(chan error, 1)
  530. downloadsDone := make(chan struct{})
  531. var cancel func()
  532. ctx, cancel = context.WithCancel(ctx)
  533. defer cancel()
  534. // Pull the image config
  535. go func() {
  536. configJSON, err := p.pullSchema2Config(ctx, target.Digest)
  537. if err != nil {
  538. configErrChan <- ImageConfigPullError{Err: err}
  539. cancel()
  540. return
  541. }
  542. configChan <- configJSON
  543. }()
  544. var (
  545. configJSON []byte // raw serialized image config
  546. downloadedRootFS *image.RootFS // rootFS from registered layers
  547. configRootFS *image.RootFS // rootFS from configuration
  548. release func() // release resources from rootFS download
  549. configPlatform *specs.Platform // for LCOW when registering downloaded layers
  550. )
  551. layerStoreOS := runtime.GOOS
  552. if platform != nil {
  553. layerStoreOS = platform.OS
  554. }
  555. // https://github.com/docker/docker/issues/24766 - Err on the side of caution,
  556. // explicitly blocking images intended for linux from the Windows daemon. On
  557. // Windows, we do this before the attempt to download, effectively serialising
  558. // the download slightly slowing it down. We have to do it this way, as
  559. // chances are the download of layers itself would fail due to file names
  560. // which aren't suitable for NTFS. At some point in the future, if a similar
  561. // check to block Windows images being pulled on Linux is implemented, it
  562. // may be necessary to perform the same type of serialisation.
  563. if runtime.GOOS == "windows" {
  564. configJSON, configRootFS, configPlatform, err = receiveConfig(p.config.ImageStore, configChan, configErrChan)
  565. if err != nil {
  566. return "", err
  567. }
  568. if configRootFS == nil {
  569. return "", errRootFSInvalid
  570. }
  571. if err := checkImageCompatibility(configPlatform.OS, configPlatform.OSVersion); err != nil {
  572. return "", err
  573. }
  574. if len(descriptors) != len(configRootFS.DiffIDs) {
  575. return "", errRootFSMismatch
  576. }
  577. if platform == nil {
  578. // Early bath if the requested OS doesn't match that of the configuration.
  579. // This avoids doing the download, only to potentially fail later.
  580. if !system.IsOSSupported(configPlatform.OS) {
  581. return "", fmt.Errorf("cannot download image with operating system %q when requesting %q", configPlatform.OS, layerStoreOS)
  582. }
  583. layerStoreOS = configPlatform.OS
  584. }
  585. // Populate diff ids in descriptors to avoid downloading foreign layers
  586. // which have been side loaded
  587. for i := range descriptors {
  588. descriptors[i].(*v2LayerDescriptor).diffID = configRootFS.DiffIDs[i]
  589. }
  590. }
  591. if p.config.DownloadManager != nil {
  592. go func() {
  593. var (
  594. err error
  595. rootFS image.RootFS
  596. )
  597. downloadRootFS := *image.NewRootFS()
  598. rootFS, release, err = p.config.DownloadManager.Download(ctx, downloadRootFS, layerStoreOS, descriptors, p.config.ProgressOutput)
  599. if err != nil {
  600. // Intentionally do not cancel the config download here
  601. // as the error from config download (if there is one)
  602. // is more interesting than the layer download error
  603. layerErrChan <- err
  604. return
  605. }
  606. downloadedRootFS = &rootFS
  607. close(downloadsDone)
  608. }()
  609. } else {
  610. // We have nothing to download
  611. close(downloadsDone)
  612. }
  613. if configJSON == nil {
  614. configJSON, configRootFS, _, err = receiveConfig(p.config.ImageStore, configChan, configErrChan)
  615. if err == nil && configRootFS == nil {
  616. err = errRootFSInvalid
  617. }
  618. if err != nil {
  619. cancel()
  620. select {
  621. case <-downloadsDone:
  622. case <-layerErrChan:
  623. }
  624. return "", err
  625. }
  626. }
  627. select {
  628. case <-downloadsDone:
  629. case err = <-layerErrChan:
  630. return "", err
  631. }
  632. if release != nil {
  633. defer release()
  634. }
  635. if downloadedRootFS != nil {
  636. // The DiffIDs returned in rootFS MUST match those in the config.
  637. // Otherwise the image config could be referencing layers that aren't
  638. // included in the manifest.
  639. if len(downloadedRootFS.DiffIDs) != len(configRootFS.DiffIDs) {
  640. return "", errRootFSMismatch
  641. }
  642. for i := range downloadedRootFS.DiffIDs {
  643. if downloadedRootFS.DiffIDs[i] != configRootFS.DiffIDs[i] {
  644. return "", errRootFSMismatch
  645. }
  646. }
  647. }
  648. imageID, err := p.config.ImageStore.Put(ctx, configJSON)
  649. if err != nil {
  650. return "", err
  651. }
  652. return imageID, nil
  653. }
  654. func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *schema2.DeserializedManifest, platform *specs.Platform) (id digest.Digest, manifestDigest digest.Digest, err error) {
  655. manifestDigest, err = schema2ManifestDigest(ref, mfst)
  656. if err != nil {
  657. return "", "", err
  658. }
  659. id, err = p.pullSchema2Layers(ctx, mfst.Target(), mfst.Layers, platform)
  660. return id, manifestDigest, err
  661. }
  662. func (p *v2Puller) pullOCI(ctx context.Context, ref reference.Named, mfst *ocischema.DeserializedManifest, platform *specs.Platform) (id digest.Digest, manifestDigest digest.Digest, err error) {
  663. manifestDigest, err = schema2ManifestDigest(ref, mfst)
  664. if err != nil {
  665. return "", "", err
  666. }
  667. id, err = p.pullSchema2Layers(ctx, mfst.Target(), mfst.Layers, platform)
  668. return id, manifestDigest, err
  669. }
  670. func receiveConfig(s ImageConfigStore, configChan <-chan []byte, errChan <-chan error) ([]byte, *image.RootFS, *specs.Platform, error) {
  671. select {
  672. case configJSON := <-configChan:
  673. rootfs, err := s.RootFSFromConfig(configJSON)
  674. if err != nil {
  675. return nil, nil, nil, err
  676. }
  677. platform, err := s.PlatformFromConfig(configJSON)
  678. if err != nil {
  679. return nil, nil, nil, err
  680. }
  681. return configJSON, rootfs, platform, nil
  682. case err := <-errChan:
  683. return nil, nil, nil, err
  684. // Don't need a case for ctx.Done in the select because cancellation
  685. // will trigger an error in p.pullSchema2ImageConfig.
  686. }
  687. }
  688. // pullManifestList handles "manifest lists" which point to various
  689. // platform-specific manifests.
  690. func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mfstList *manifestlist.DeserializedManifestList, pp *specs.Platform) (id digest.Digest, manifestListDigest digest.Digest, err error) {
  691. manifestListDigest, err = schema2ManifestDigest(ref, mfstList)
  692. if err != nil {
  693. return "", "", err
  694. }
  695. var platform specs.Platform
  696. if pp != nil {
  697. platform = *pp
  698. }
  699. logrus.Debugf("%s resolved to a manifestList object with %d entries; looking for a %s/%s match", ref, len(mfstList.Manifests), platforms.Format(platform), runtime.GOARCH)
  700. manifestMatches := filterManifests(mfstList.Manifests, platform)
  701. if len(manifestMatches) == 0 {
  702. errMsg := fmt.Sprintf("no matching manifest for %s in the manifest list entries", formatPlatform(platform))
  703. logrus.Debugf(errMsg)
  704. return "", "", errors.New(errMsg)
  705. }
  706. if len(manifestMatches) > 1 {
  707. logrus.Debugf("found multiple matches in manifest list, choosing best match %s", manifestMatches[0].Digest.String())
  708. }
  709. match := manifestMatches[0]
  710. if err := checkImageCompatibility(match.Platform.OS, match.Platform.OSVersion); err != nil {
  711. return "", "", err
  712. }
  713. manifest, err := p.manifestStore.Get(ctx, specs.Descriptor{
  714. Digest: match.Digest,
  715. Size: match.Size,
  716. MediaType: match.MediaType,
  717. })
  718. if err != nil {
  719. return "", "", err
  720. }
  721. manifestRef, err := reference.WithDigest(reference.TrimNamed(ref), match.Digest)
  722. if err != nil {
  723. return "", "", err
  724. }
  725. switch v := manifest.(type) {
  726. case *schema1.SignedManifest:
  727. msg := fmt.Sprintf("[DEPRECATION NOTICE] v2 schema1 manifests in manifest lists are not supported and will break in a future release. Suggest author of %s to upgrade to v2 schema2. More information at https://docs.docker.com/registry/spec/deprecated-schema-v1/", ref)
  728. logrus.Warn(msg)
  729. progress.Message(p.config.ProgressOutput, "", msg)
  730. platform := toOCIPlatform(manifestMatches[0].Platform)
  731. id, _, err = p.pullSchema1(ctx, manifestRef, v, &platform)
  732. if err != nil {
  733. return "", "", err
  734. }
  735. case *schema2.DeserializedManifest:
  736. platform := toOCIPlatform(manifestMatches[0].Platform)
  737. id, _, err = p.pullSchema2(ctx, manifestRef, v, &platform)
  738. if err != nil {
  739. return "", "", err
  740. }
  741. case *ocischema.DeserializedManifest:
  742. platform := toOCIPlatform(manifestMatches[0].Platform)
  743. id, _, err = p.pullOCI(ctx, manifestRef, v, &platform)
  744. if err != nil {
  745. return "", "", err
  746. }
  747. default:
  748. return "", "", errors.New("unsupported manifest format")
  749. }
  750. return id, manifestListDigest, err
  751. }
  752. func (p *v2Puller) pullSchema2Config(ctx context.Context, dgst digest.Digest) (configJSON []byte, err error) {
  753. blobs := p.repo.Blobs(ctx)
  754. configJSON, err = blobs.Get(ctx, dgst)
  755. if err != nil {
  756. return nil, err
  757. }
  758. // Verify image config digest
  759. verifier := dgst.Verifier()
  760. if _, err := verifier.Write(configJSON); err != nil {
  761. return nil, err
  762. }
  763. if !verifier.Verified() {
  764. err := fmt.Errorf("image config verification failed for digest %s", dgst)
  765. logrus.Error(err)
  766. return nil, err
  767. }
  768. return configJSON, nil
  769. }
  770. // schema2ManifestDigest computes the manifest digest, and, if pulling by
  771. // digest, ensures that it matches the requested digest.
  772. func schema2ManifestDigest(ref reference.Named, mfst distribution.Manifest) (digest.Digest, error) {
  773. _, canonical, err := mfst.Payload()
  774. if err != nil {
  775. return "", err
  776. }
  777. // If pull by digest, then verify the manifest digest.
  778. if digested, isDigested := ref.(reference.Canonical); isDigested {
  779. verifier := digested.Digest().Verifier()
  780. if _, err := verifier.Write(canonical); err != nil {
  781. return "", err
  782. }
  783. if !verifier.Verified() {
  784. err := fmt.Errorf("manifest verification failed for digest %s", digested.Digest())
  785. logrus.Error(err)
  786. return "", err
  787. }
  788. return digested.Digest(), nil
  789. }
  790. return digest.FromBytes(canonical), nil
  791. }
  792. // allowV1Fallback checks if the error is a possible reason to fallback to v1
  793. // (even if confirmedV2 has been set already), and if so, wraps the error in
  794. // a fallbackError with confirmedV2 set to false. Otherwise, it returns the
  795. // error unmodified.
  796. func allowV1Fallback(err error) error {
  797. switch v := err.(type) {
  798. case errcode.Errors:
  799. if len(v) != 0 {
  800. if v0, ok := v[0].(errcode.Error); ok && shouldV2Fallback(v0) {
  801. return fallbackError{
  802. err: err,
  803. confirmedV2: false,
  804. transportOK: true,
  805. }
  806. }
  807. }
  808. case errcode.Error:
  809. if shouldV2Fallback(v) {
  810. return fallbackError{
  811. err: err,
  812. confirmedV2: false,
  813. transportOK: true,
  814. }
  815. }
  816. case *url.Error:
  817. if v.Err == auth.ErrNoBasicAuthCredentials {
  818. return fallbackError{err: err, confirmedV2: false}
  819. }
  820. }
  821. return err
  822. }
  823. func verifySchema1Manifest(signedManifest *schema1.SignedManifest, ref reference.Reference) (m *schema1.Manifest, err error) {
  824. // If pull by digest, then verify the manifest digest. NOTE: It is
  825. // important to do this first, before any other content validation. If the
  826. // digest cannot be verified, don't even bother with those other things.
  827. if digested, isCanonical := ref.(reference.Canonical); isCanonical {
  828. verifier := digested.Digest().Verifier()
  829. if _, err := verifier.Write(signedManifest.Canonical); err != nil {
  830. return nil, err
  831. }
  832. if !verifier.Verified() {
  833. err := fmt.Errorf("image verification failed for digest %s", digested.Digest())
  834. logrus.Error(err)
  835. return nil, err
  836. }
  837. }
  838. m = &signedManifest.Manifest
  839. if m.SchemaVersion != 1 {
  840. return nil, fmt.Errorf("unsupported schema version %d for %q", m.SchemaVersion, reference.FamiliarString(ref))
  841. }
  842. if len(m.FSLayers) != len(m.History) {
  843. return nil, fmt.Errorf("length of history not equal to number of layers for %q", reference.FamiliarString(ref))
  844. }
  845. if len(m.FSLayers) == 0 {
  846. return nil, fmt.Errorf("no FSLayers in manifest for %q", reference.FamiliarString(ref))
  847. }
  848. return m, nil
  849. }
  850. // fixManifestLayers removes repeated layers from the manifest and checks the
  851. // correctness of the parent chain.
  852. func fixManifestLayers(m *schema1.Manifest) error {
  853. imgs := make([]*image.V1Image, len(m.FSLayers))
  854. for i := range m.FSLayers {
  855. img := &image.V1Image{}
  856. if err := json.Unmarshal([]byte(m.History[i].V1Compatibility), img); err != nil {
  857. return err
  858. }
  859. imgs[i] = img
  860. if err := v1.ValidateID(img.ID); err != nil {
  861. return err
  862. }
  863. }
  864. if imgs[len(imgs)-1].Parent != "" && runtime.GOOS != "windows" {
  865. // Windows base layer can point to a base layer parent that is not in manifest.
  866. return errors.New("invalid parent ID in the base layer of the image")
  867. }
  868. // check general duplicates to error instead of a deadlock
  869. idmap := make(map[string]struct{})
  870. var lastID string
  871. for _, img := range imgs {
  872. // skip IDs that appear after each other, we handle those later
  873. if _, exists := idmap[img.ID]; img.ID != lastID && exists {
  874. return fmt.Errorf("ID %+v appears multiple times in manifest", img.ID)
  875. }
  876. lastID = img.ID
  877. idmap[lastID] = struct{}{}
  878. }
  879. // backwards loop so that we keep the remaining indexes after removing items
  880. for i := len(imgs) - 2; i >= 0; i-- {
  881. if imgs[i].ID == imgs[i+1].ID { // repeated ID. remove and continue
  882. m.FSLayers = append(m.FSLayers[:i], m.FSLayers[i+1:]...)
  883. m.History = append(m.History[:i], m.History[i+1:]...)
  884. } else if imgs[i].Parent != imgs[i+1].ID {
  885. return fmt.Errorf("invalid parent ID. Expected %v, got %v", imgs[i+1].ID, imgs[i].Parent)
  886. }
  887. }
  888. return nil
  889. }
  890. func createDownloadFile() (*os.File, error) {
  891. return ioutil.TempFile("", "GetImageBlob")
  892. }
  893. func toOCIPlatform(p manifestlist.PlatformSpec) specs.Platform {
  894. return specs.Platform{
  895. OS: p.OS,
  896. Architecture: p.Architecture,
  897. Variant: p.Variant,
  898. OSFeatures: p.OSFeatures,
  899. OSVersion: p.OSVersion,
  900. }
  901. }