pull_v2.go 33 KB

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