github.com/portworx/docker@v1.12.1/api/client/trust.go (about)

     1  package client
     2  
     3  import (
     4  	"encoding/hex"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"net"
    10  	"net/http"
    11  	"net/url"
    12  	"os"
    13  	"path"
    14  	"path/filepath"
    15  	"sort"
    16  	"strconv"
    17  	"time"
    18  
    19  	"golang.org/x/net/context"
    20  
    21  	"github.com/Sirupsen/logrus"
    22  	"github.com/docker/distribution/digest"
    23  	"github.com/docker/distribution/registry/client/auth"
    24  	"github.com/docker/distribution/registry/client/transport"
    25  	"github.com/docker/docker/cliconfig"
    26  	"github.com/docker/docker/distribution"
    27  	"github.com/docker/docker/pkg/jsonmessage"
    28  	flag "github.com/docker/docker/pkg/mflag"
    29  	"github.com/docker/docker/reference"
    30  	"github.com/docker/docker/registry"
    31  	"github.com/docker/engine-api/types"
    32  	registrytypes "github.com/docker/engine-api/types/registry"
    33  	"github.com/docker/go-connections/tlsconfig"
    34  	"github.com/docker/notary/client"
    35  	"github.com/docker/notary/passphrase"
    36  	"github.com/docker/notary/trustmanager"
    37  	"github.com/docker/notary/trustpinning"
    38  	"github.com/docker/notary/tuf/data"
    39  	"github.com/docker/notary/tuf/signed"
    40  	"github.com/docker/notary/tuf/store"
    41  	"github.com/spf13/pflag"
    42  )
    43  
    44  var (
    45  	releasesRole = path.Join(data.CanonicalTargetsRole, "releases")
    46  	untrusted    bool
    47  )
    48  
    49  // addTrustedFlags is the mflag version of AddTrustedFlags
    50  func addTrustedFlags(fs *flag.FlagSet, verify bool) {
    51  	trusted, message := setupTrustedFlag(verify)
    52  	fs.BoolVar(&untrusted, []string{"-disable-content-trust"}, !trusted, message)
    53  }
    54  
    55  // AddTrustedFlags adds content trust flags to the current command flagset
    56  func AddTrustedFlags(fs *pflag.FlagSet, verify bool) {
    57  	trusted, message := setupTrustedFlag(verify)
    58  	fs.BoolVar(&untrusted, "disable-content-trust", !trusted, message)
    59  }
    60  
    61  func setupTrustedFlag(verify bool) (bool, string) {
    62  	var trusted bool
    63  	if e := os.Getenv("DOCKER_CONTENT_TRUST"); e != "" {
    64  		if t, err := strconv.ParseBool(e); t || err != nil {
    65  			// treat any other value as true
    66  			trusted = true
    67  		}
    68  	}
    69  	message := "Skip image signing"
    70  	if verify {
    71  		message = "Skip image verification"
    72  	}
    73  	return trusted, message
    74  }
    75  
    76  // IsTrusted returns true if content trust is enabled
    77  func IsTrusted() bool {
    78  	return !untrusted
    79  }
    80  
    81  type target struct {
    82  	reference registry.Reference
    83  	digest    digest.Digest
    84  	size      int64
    85  }
    86  
    87  func (cli *DockerCli) trustDirectory() string {
    88  	return filepath.Join(cliconfig.ConfigDir(), "trust")
    89  }
    90  
    91  // certificateDirectory returns the directory containing
    92  // TLS certificates for the given server. An error is
    93  // returned if there was an error parsing the server string.
    94  func (cli *DockerCli) certificateDirectory(server string) (string, error) {
    95  	u, err := url.Parse(server)
    96  	if err != nil {
    97  		return "", err
    98  	}
    99  
   100  	return filepath.Join(cliconfig.ConfigDir(), "tls", u.Host), nil
   101  }
   102  
   103  func trustServer(index *registrytypes.IndexInfo) (string, error) {
   104  	if s := os.Getenv("DOCKER_CONTENT_TRUST_SERVER"); s != "" {
   105  		urlObj, err := url.Parse(s)
   106  		if err != nil || urlObj.Scheme != "https" {
   107  			return "", fmt.Errorf("valid https URL required for trust server, got %s", s)
   108  		}
   109  
   110  		return s, nil
   111  	}
   112  	if index.Official {
   113  		return registry.NotaryServer, nil
   114  	}
   115  	return "https://" + index.Name, nil
   116  }
   117  
   118  type simpleCredentialStore struct {
   119  	auth types.AuthConfig
   120  }
   121  
   122  func (scs simpleCredentialStore) Basic(u *url.URL) (string, string) {
   123  	return scs.auth.Username, scs.auth.Password
   124  }
   125  
   126  func (scs simpleCredentialStore) RefreshToken(u *url.URL, service string) string {
   127  	return scs.auth.IdentityToken
   128  }
   129  
   130  func (scs simpleCredentialStore) SetRefreshToken(*url.URL, string, string) {
   131  }
   132  
   133  // getNotaryRepository returns a NotaryRepository which stores all the
   134  // information needed to operate on a notary repository.
   135  // It creates an HTTP transport providing authentication support.
   136  func (cli *DockerCli) getNotaryRepository(repoInfo *registry.RepositoryInfo, authConfig types.AuthConfig, actions ...string) (*client.NotaryRepository, error) {
   137  	server, err := trustServer(repoInfo.Index)
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  
   142  	var cfg = tlsconfig.ClientDefault
   143  	cfg.InsecureSkipVerify = !repoInfo.Index.Secure
   144  
   145  	// Get certificate base directory
   146  	certDir, err := cli.certificateDirectory(server)
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  	logrus.Debugf("reading certificate directory: %s", certDir)
   151  
   152  	if err := registry.ReadCertsDirectory(&cfg, certDir); err != nil {
   153  		return nil, err
   154  	}
   155  
   156  	base := &http.Transport{
   157  		Proxy: http.ProxyFromEnvironment,
   158  		Dial: (&net.Dialer{
   159  			Timeout:   30 * time.Second,
   160  			KeepAlive: 30 * time.Second,
   161  			DualStack: true,
   162  		}).Dial,
   163  		TLSHandshakeTimeout: 10 * time.Second,
   164  		TLSClientConfig:     &cfg,
   165  		DisableKeepAlives:   true,
   166  	}
   167  
   168  	// Skip configuration headers since request is not going to Docker daemon
   169  	modifiers := registry.DockerHeaders(clientUserAgent(), http.Header{})
   170  	authTransport := transport.NewTransport(base, modifiers...)
   171  	pingClient := &http.Client{
   172  		Transport: authTransport,
   173  		Timeout:   5 * time.Second,
   174  	}
   175  	endpointStr := server + "/v2/"
   176  	req, err := http.NewRequest("GET", endpointStr, nil)
   177  	if err != nil {
   178  		return nil, err
   179  	}
   180  
   181  	challengeManager := auth.NewSimpleChallengeManager()
   182  
   183  	resp, err := pingClient.Do(req)
   184  	if err != nil {
   185  		// Ignore error on ping to operate in offline mode
   186  		logrus.Debugf("Error pinging notary server %q: %s", endpointStr, err)
   187  	} else {
   188  		defer resp.Body.Close()
   189  
   190  		// Add response to the challenge manager to parse out
   191  		// authentication header and register authentication method
   192  		if err := challengeManager.AddResponse(resp); err != nil {
   193  			return nil, err
   194  		}
   195  	}
   196  
   197  	creds := simpleCredentialStore{auth: authConfig}
   198  	tokenHandler := auth.NewTokenHandler(authTransport, creds, repoInfo.FullName(), actions...)
   199  	basicHandler := auth.NewBasicHandler(creds)
   200  	modifiers = append(modifiers, transport.RequestModifier(auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler)))
   201  	tr := transport.NewTransport(base, modifiers...)
   202  
   203  	return client.NewNotaryRepository(
   204  		cli.trustDirectory(), repoInfo.FullName(), server, tr, cli.getPassphraseRetriever(),
   205  		trustpinning.TrustPinConfig{})
   206  }
   207  
   208  func convertTarget(t client.Target) (target, error) {
   209  	h, ok := t.Hashes["sha256"]
   210  	if !ok {
   211  		return target{}, errors.New("no valid hash, expecting sha256")
   212  	}
   213  	return target{
   214  		reference: registry.ParseReference(t.Name),
   215  		digest:    digest.NewDigestFromHex("sha256", hex.EncodeToString(h)),
   216  		size:      t.Length,
   217  	}, nil
   218  }
   219  
   220  func (cli *DockerCli) getPassphraseRetriever() passphrase.Retriever {
   221  	aliasMap := map[string]string{
   222  		"root":     "root",
   223  		"snapshot": "repository",
   224  		"targets":  "repository",
   225  		"default":  "repository",
   226  	}
   227  	baseRetriever := passphrase.PromptRetrieverWithInOut(cli.in, cli.out, aliasMap)
   228  	env := map[string]string{
   229  		"root":     os.Getenv("DOCKER_CONTENT_TRUST_ROOT_PASSPHRASE"),
   230  		"snapshot": os.Getenv("DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE"),
   231  		"targets":  os.Getenv("DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE"),
   232  		"default":  os.Getenv("DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE"),
   233  	}
   234  
   235  	return func(keyName string, alias string, createNew bool, numAttempts int) (string, bool, error) {
   236  		if v := env[alias]; v != "" {
   237  			return v, numAttempts > 1, nil
   238  		}
   239  		// For non-root roles, we can also try the "default" alias if it is specified
   240  		if v := env["default"]; v != "" && alias != data.CanonicalRootRole {
   241  			return v, numAttempts > 1, nil
   242  		}
   243  		return baseRetriever(keyName, alias, createNew, numAttempts)
   244  	}
   245  }
   246  
   247  // TrustedReference returns the canonical trusted reference for an image reference
   248  func (cli *DockerCli) TrustedReference(ctx context.Context, ref reference.NamedTagged) (reference.Canonical, error) {
   249  	repoInfo, err := registry.ParseRepositoryInfo(ref)
   250  	if err != nil {
   251  		return nil, err
   252  	}
   253  
   254  	// Resolve the Auth config relevant for this server
   255  	authConfig := cli.ResolveAuthConfig(ctx, repoInfo.Index)
   256  
   257  	notaryRepo, err := cli.getNotaryRepository(repoInfo, authConfig, "pull")
   258  	if err != nil {
   259  		fmt.Fprintf(cli.out, "Error establishing connection to trust repository: %s\n", err)
   260  		return nil, err
   261  	}
   262  
   263  	t, err := notaryRepo.GetTargetByName(ref.Tag(), releasesRole, data.CanonicalTargetsRole)
   264  	if err != nil {
   265  		return nil, err
   266  	}
   267  	// Only list tags in the top level targets role or the releases delegation role - ignore
   268  	// all other delegation roles
   269  	if t.Role != releasesRole && t.Role != data.CanonicalTargetsRole {
   270  		return nil, notaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", ref.Tag()))
   271  	}
   272  	r, err := convertTarget(t.Target)
   273  	if err != nil {
   274  		return nil, err
   275  
   276  	}
   277  
   278  	return reference.WithDigest(ref, r.digest)
   279  }
   280  
   281  // TagTrusted tags a trusted ref
   282  func (cli *DockerCli) TagTrusted(ctx context.Context, trustedRef reference.Canonical, ref reference.NamedTagged) error {
   283  	fmt.Fprintf(cli.out, "Tagging %s as %s\n", trustedRef.String(), ref.String())
   284  
   285  	return cli.client.ImageTag(ctx, trustedRef.String(), ref.String())
   286  }
   287  
   288  func notaryError(repoName string, err error) error {
   289  	switch err.(type) {
   290  	case *json.SyntaxError:
   291  		logrus.Debugf("Notary syntax error: %s", err)
   292  		return fmt.Errorf("Error: no trust data available for remote repository %s. Try running notary server and setting DOCKER_CONTENT_TRUST_SERVER to its HTTPS address?", repoName)
   293  	case signed.ErrExpired:
   294  		return fmt.Errorf("Error: remote repository %s out-of-date: %v", repoName, err)
   295  	case trustmanager.ErrKeyNotFound:
   296  		return fmt.Errorf("Error: signing keys for remote repository %s not found: %v", repoName, err)
   297  	case *net.OpError:
   298  		return fmt.Errorf("Error: error contacting notary server: %v", err)
   299  	case store.ErrMetaNotFound:
   300  		return fmt.Errorf("Error: trust data missing for remote repository %s or remote repository not found: %v", repoName, err)
   301  	case signed.ErrInvalidKeyType:
   302  		return fmt.Errorf("Warning: potential malicious behavior - trust data mismatch for remote repository %s: %v", repoName, err)
   303  	case signed.ErrNoKeys:
   304  		return fmt.Errorf("Error: could not find signing keys for remote repository %s, or could not decrypt signing key: %v", repoName, err)
   305  	case signed.ErrLowVersion:
   306  		return fmt.Errorf("Warning: potential malicious behavior - trust data version is lower than expected for remote repository %s: %v", repoName, err)
   307  	case signed.ErrRoleThreshold:
   308  		return fmt.Errorf("Warning: potential malicious behavior - trust data has insufficient signatures for remote repository %s: %v", repoName, err)
   309  	case client.ErrRepositoryNotExist:
   310  		return fmt.Errorf("Error: remote trust data does not exist for %s: %v", repoName, err)
   311  	case signed.ErrInsufficientSignatures:
   312  		return fmt.Errorf("Error: could not produce valid signature for %s.  If Yubikey was used, was touch input provided?: %v", repoName, err)
   313  	}
   314  
   315  	return err
   316  }
   317  
   318  // TrustedPull handles content trust pulling of an image
   319  func (cli *DockerCli) TrustedPull(ctx context.Context, repoInfo *registry.RepositoryInfo, ref registry.Reference, authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error {
   320  	var refs []target
   321  
   322  	notaryRepo, err := cli.getNotaryRepository(repoInfo, authConfig, "pull")
   323  	if err != nil {
   324  		fmt.Fprintf(cli.out, "Error establishing connection to trust repository: %s\n", err)
   325  		return err
   326  	}
   327  
   328  	if ref.String() == "" {
   329  		// List all targets
   330  		targets, err := notaryRepo.ListTargets(releasesRole, data.CanonicalTargetsRole)
   331  		if err != nil {
   332  			return notaryError(repoInfo.FullName(), err)
   333  		}
   334  		for _, tgt := range targets {
   335  			t, err := convertTarget(tgt.Target)
   336  			if err != nil {
   337  				fmt.Fprintf(cli.out, "Skipping target for %q\n", repoInfo.Name())
   338  				continue
   339  			}
   340  			// Only list tags in the top level targets role or the releases delegation role - ignore
   341  			// all other delegation roles
   342  			if tgt.Role != releasesRole && tgt.Role != data.CanonicalTargetsRole {
   343  				continue
   344  			}
   345  			refs = append(refs, t)
   346  		}
   347  		if len(refs) == 0 {
   348  			return notaryError(repoInfo.FullName(), fmt.Errorf("No trusted tags for %s", repoInfo.FullName()))
   349  		}
   350  	} else {
   351  		t, err := notaryRepo.GetTargetByName(ref.String(), releasesRole, data.CanonicalTargetsRole)
   352  		if err != nil {
   353  			return notaryError(repoInfo.FullName(), err)
   354  		}
   355  		// Only get the tag if it's in the top level targets role or the releases delegation role
   356  		// ignore it if it's in any other delegation roles
   357  		if t.Role != releasesRole && t.Role != data.CanonicalTargetsRole {
   358  			return notaryError(repoInfo.FullName(), fmt.Errorf("No trust data for %s", ref.String()))
   359  		}
   360  
   361  		logrus.Debugf("retrieving target for %s role\n", t.Role)
   362  		r, err := convertTarget(t.Target)
   363  		if err != nil {
   364  			return err
   365  
   366  		}
   367  		refs = append(refs, r)
   368  	}
   369  
   370  	for i, r := range refs {
   371  		displayTag := r.reference.String()
   372  		if displayTag != "" {
   373  			displayTag = ":" + displayTag
   374  		}
   375  		fmt.Fprintf(cli.out, "Pull (%d of %d): %s%s@%s\n", i+1, len(refs), repoInfo.Name(), displayTag, r.digest)
   376  
   377  		ref, err := reference.WithDigest(repoInfo, r.digest)
   378  		if err != nil {
   379  			return err
   380  		}
   381  		if err := cli.ImagePullPrivileged(ctx, authConfig, ref.String(), requestPrivilege, false); err != nil {
   382  			return err
   383  		}
   384  
   385  		// If reference is not trusted, tag by trusted reference
   386  		if !r.reference.HasDigest() {
   387  			tagged, err := reference.WithTag(repoInfo, r.reference.String())
   388  			if err != nil {
   389  				return err
   390  			}
   391  			trustedRef, err := reference.WithDigest(repoInfo, r.digest)
   392  			if err != nil {
   393  				return err
   394  			}
   395  			if err := cli.TagTrusted(ctx, trustedRef, tagged); err != nil {
   396  				return err
   397  			}
   398  		}
   399  	}
   400  	return nil
   401  }
   402  
   403  // TrustedPush handles content trust pushing of an image
   404  func (cli *DockerCli) TrustedPush(ctx context.Context, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error {
   405  	responseBody, err := cli.ImagePushPrivileged(ctx, authConfig, ref.String(), requestPrivilege)
   406  	if err != nil {
   407  		return err
   408  	}
   409  
   410  	defer responseBody.Close()
   411  
   412  	// If it is a trusted push we would like to find the target entry which match the
   413  	// tag provided in the function and then do an AddTarget later.
   414  	target := &client.Target{}
   415  	// Count the times of calling for handleTarget,
   416  	// if it is called more that once, that should be considered an error in a trusted push.
   417  	cnt := 0
   418  	handleTarget := func(aux *json.RawMessage) {
   419  		cnt++
   420  		if cnt > 1 {
   421  			// handleTarget should only be called one. This will be treated as an error.
   422  			return
   423  		}
   424  
   425  		var pushResult distribution.PushResult
   426  		err := json.Unmarshal(*aux, &pushResult)
   427  		if err == nil && pushResult.Tag != "" && pushResult.Digest.Validate() == nil {
   428  			h, err := hex.DecodeString(pushResult.Digest.Hex())
   429  			if err != nil {
   430  				target = nil
   431  				return
   432  			}
   433  			target.Name = registry.ParseReference(pushResult.Tag).String()
   434  			target.Hashes = data.Hashes{string(pushResult.Digest.Algorithm()): h}
   435  			target.Length = int64(pushResult.Size)
   436  		}
   437  	}
   438  
   439  	var tag string
   440  	switch x := ref.(type) {
   441  	case reference.Canonical:
   442  		return errors.New("cannot push a digest reference")
   443  	case reference.NamedTagged:
   444  		tag = x.Tag()
   445  	}
   446  
   447  	// We want trust signatures to always take an explicit tag,
   448  	// otherwise it will act as an untrusted push.
   449  	if tag == "" {
   450  		if err = jsonmessage.DisplayJSONMessagesStream(responseBody, cli.out, cli.outFd, cli.isTerminalOut, nil); err != nil {
   451  			return err
   452  		}
   453  		fmt.Fprintln(cli.out, "No tag specified, skipping trust metadata push")
   454  		return nil
   455  	}
   456  
   457  	if err = jsonmessage.DisplayJSONMessagesStream(responseBody, cli.out, cli.outFd, cli.isTerminalOut, handleTarget); err != nil {
   458  		return err
   459  	}
   460  
   461  	if cnt > 1 {
   462  		return fmt.Errorf("internal error: only one call to handleTarget expected")
   463  	}
   464  
   465  	if target == nil {
   466  		fmt.Fprintln(cli.out, "No targets found, please provide a specific tag in order to sign it")
   467  		return nil
   468  	}
   469  
   470  	fmt.Fprintln(cli.out, "Signing and pushing trust metadata")
   471  
   472  	repo, err := cli.getNotaryRepository(repoInfo, authConfig, "push", "pull")
   473  	if err != nil {
   474  		fmt.Fprintf(cli.out, "Error establishing connection to notary repository: %s\n", err)
   475  		return err
   476  	}
   477  
   478  	// get the latest repository metadata so we can figure out which roles to sign
   479  	err = repo.Update(false)
   480  
   481  	switch err.(type) {
   482  	case client.ErrRepoNotInitialized, client.ErrRepositoryNotExist:
   483  		keys := repo.CryptoService.ListKeys(data.CanonicalRootRole)
   484  		var rootKeyID string
   485  		// always select the first root key
   486  		if len(keys) > 0 {
   487  			sort.Strings(keys)
   488  			rootKeyID = keys[0]
   489  		} else {
   490  			rootPublicKey, err := repo.CryptoService.Create(data.CanonicalRootRole, "", data.ECDSAKey)
   491  			if err != nil {
   492  				return err
   493  			}
   494  			rootKeyID = rootPublicKey.ID()
   495  		}
   496  
   497  		// Initialize the notary repository with a remotely managed snapshot key
   498  		if err := repo.Initialize(rootKeyID, data.CanonicalSnapshotRole); err != nil {
   499  			return notaryError(repoInfo.FullName(), err)
   500  		}
   501  		fmt.Fprintf(cli.out, "Finished initializing %q\n", repoInfo.FullName())
   502  		err = repo.AddTarget(target, data.CanonicalTargetsRole)
   503  	case nil:
   504  		// already initialized and we have successfully downloaded the latest metadata
   505  		err = cli.addTargetToAllSignableRoles(repo, target)
   506  	default:
   507  		return notaryError(repoInfo.FullName(), err)
   508  	}
   509  
   510  	if err == nil {
   511  		err = repo.Publish()
   512  	}
   513  
   514  	if err != nil {
   515  		fmt.Fprintf(cli.out, "Failed to sign %q:%s - %s\n", repoInfo.FullName(), tag, err.Error())
   516  		return notaryError(repoInfo.FullName(), err)
   517  	}
   518  
   519  	fmt.Fprintf(cli.out, "Successfully signed %q:%s\n", repoInfo.FullName(), tag)
   520  	return nil
   521  }
   522  
   523  // Attempt to add the image target to all the top level delegation roles we can
   524  // (based on whether we have the signing key and whether the role's path allows
   525  // us to).
   526  // If there are no delegation roles, we add to the targets role.
   527  func (cli *DockerCli) addTargetToAllSignableRoles(repo *client.NotaryRepository, target *client.Target) error {
   528  	var signableRoles []string
   529  
   530  	// translate the full key names, which includes the GUN, into just the key IDs
   531  	allCanonicalKeyIDs := make(map[string]struct{})
   532  	for fullKeyID := range repo.CryptoService.ListAllKeys() {
   533  		allCanonicalKeyIDs[path.Base(fullKeyID)] = struct{}{}
   534  	}
   535  
   536  	allDelegationRoles, err := repo.GetDelegationRoles()
   537  	if err != nil {
   538  		return err
   539  	}
   540  
   541  	// if there are no delegation roles, then just try to sign it into the targets role
   542  	if len(allDelegationRoles) == 0 {
   543  		return repo.AddTarget(target, data.CanonicalTargetsRole)
   544  	}
   545  
   546  	// there are delegation roles, find every delegation role we have a key for, and
   547  	// attempt to sign into into all those roles.
   548  	for _, delegationRole := range allDelegationRoles {
   549  		// We do not support signing any delegation role that isn't a direct child of the targets role.
   550  		// Also don't bother checking the keys if we can't add the target
   551  		// to this role due to path restrictions
   552  		if path.Dir(delegationRole.Name) != data.CanonicalTargetsRole || !delegationRole.CheckPaths(target.Name) {
   553  			continue
   554  		}
   555  
   556  		for _, canonicalKeyID := range delegationRole.KeyIDs {
   557  			if _, ok := allCanonicalKeyIDs[canonicalKeyID]; ok {
   558  				signableRoles = append(signableRoles, delegationRole.Name)
   559  				break
   560  			}
   561  		}
   562  	}
   563  
   564  	if len(signableRoles) == 0 {
   565  		return fmt.Errorf("no valid signing keys for delegation roles")
   566  	}
   567  
   568  	return repo.AddTarget(target, signableRoles...)
   569  }
   570  
   571  // ImagePullPrivileged pulls the image and displays it to the output
   572  func (cli *DockerCli) ImagePullPrivileged(ctx context.Context, authConfig types.AuthConfig, ref string, requestPrivilege types.RequestPrivilegeFunc, all bool) error {
   573  
   574  	encodedAuth, err := EncodeAuthToBase64(authConfig)
   575  	if err != nil {
   576  		return err
   577  	}
   578  	options := types.ImagePullOptions{
   579  		RegistryAuth:  encodedAuth,
   580  		PrivilegeFunc: requestPrivilege,
   581  		All:           all,
   582  	}
   583  
   584  	responseBody, err := cli.client.ImagePull(ctx, ref, options)
   585  	if err != nil {
   586  		return err
   587  	}
   588  	defer responseBody.Close()
   589  
   590  	return jsonmessage.DisplayJSONMessagesStream(responseBody, cli.out, cli.outFd, cli.isTerminalOut, nil)
   591  }
   592  
   593  // ImagePushPrivileged push the image
   594  func (cli *DockerCli) ImagePushPrivileged(ctx context.Context, authConfig types.AuthConfig, ref string, requestPrivilege types.RequestPrivilegeFunc) (io.ReadCloser, error) {
   595  	encodedAuth, err := EncodeAuthToBase64(authConfig)
   596  	if err != nil {
   597  		return nil, err
   598  	}
   599  	options := types.ImagePushOptions{
   600  		RegistryAuth:  encodedAuth,
   601  		PrivilegeFunc: requestPrivilege,
   602  	}
   603  
   604  	return cli.client.ImagePush(ctx, ref, options)
   605  }