@@ -209,6 +209,28 @@ func (d *Descriptor) remoteIndex() *remoteIndex {
209209 }
210210}
211211
212+ // https://github.com/docker/hub-feedback/issues/2107#issuecomment-1371293316
213+ //
214+ // DockerHub supports plugins, which look like normal manifests, but will
215+ // return a 401 with an incorrect challenge if you attempt to fetch them.
216+ //
217+ // They require you send, e.g.:
218+ // 'repository(plugin):vieux/sshfs:pull' not 'repository:vieux/sshfs:pull'.
219+ //
220+ // Hack around this by always including the plugin-ified version in the initial
221+ // scopes. The request will succeed with the correct subset, so it is safe to
222+ // have extraneous scopes here.
223+ func fixPluginScopes (ref name.Reference , scopes []string ) []string {
224+ if ref .Context ().Registry .String () == name .DefaultRegistry {
225+ for _ , scope := range scopes {
226+ if strings .HasPrefix (scope , "repository" ) {
227+ scopes = append (scopes , strings .Replace (scope , "repository" , "repository(plugin)" , 1 ))
228+ }
229+ }
230+ }
231+ return scopes
232+ }
233+
212234// fetcher implements methods for reading from a registry.
213235type fetcher struct {
214236 Ref name.Reference
@@ -217,7 +239,10 @@ type fetcher struct {
217239}
218240
219241func makeFetcher (ref name.Reference , o * options ) (* fetcher , error ) {
220- tr , err := transport .NewWithContext (o .context , ref .Context ().Registry , o .auth , o .transport , []string {ref .Scope (transport .PullScope )})
242+ scopes := []string {ref .Scope (transport .PullScope )}
243+ scopes = fixPluginScopes (ref , scopes )
244+
245+ tr , err := transport .NewWithContext (o .context , ref .Context ().Registry , o .auth , o .transport , scopes )
221246 if err != nil {
222247 return nil , err
223248 }
0 commit comments