2017-08-24 18:46:01 -04:00
package trust
import (
2017-08-25 17:49:40 -04:00
"context"
2017-08-24 18:46:01 -04:00
"fmt"
"os"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
2017-09-26 12:53:21 -04:00
"github.com/docker/cli/cli/command/image"
2017-08-24 18:46:01 -04:00
"github.com/docker/cli/cli/trust"
2017-09-26 12:53:21 -04:00
"github.com/pkg/errors"
2017-08-24 18:46:01 -04:00
"github.com/spf13/cobra"
2017-10-30 12:21:41 -04:00
"github.com/theupdateframework/notary/client"
"github.com/theupdateframework/notary/tuf/data"
2017-08-24 18:46:01 -04:00
)
type revokeOptions struct {
forceYes bool
}
func newRevokeCommand ( dockerCli command . Cli ) * cobra . Command {
options := revokeOptions { }
cmd := & cobra . Command {
Use : "revoke [OPTIONS] IMAGE[:TAG]" ,
Short : "Remove trust for an image" ,
Args : cli . ExactArgs ( 1 ) ,
RunE : func ( cmd * cobra . Command , args [ ] string ) error {
return revokeTrust ( dockerCli , args [ 0 ] , options )
} ,
}
flags := cmd . Flags ( )
2017-08-25 17:49:40 -04:00
flags . BoolVarP ( & options . forceYes , "yes" , "y" , false , "Do not prompt for confirmation" )
2017-08-24 18:46:01 -04:00
return cmd
}
func revokeTrust ( cli command . Cli , remote string , options revokeOptions ) error {
2017-08-25 17:49:40 -04:00
ctx := context . Background ( )
2018-03-06 05:15:18 -05:00
imgRefAndAuth , err := trust . GetImageReferencesAndAuth ( ctx , nil , image . AuthResolver ( cli ) , remote )
2017-08-24 18:46:01 -04:00
if err != nil {
return err
}
2017-08-25 17:49:40 -04:00
tag := imgRefAndAuth . Tag ( )
2017-09-12 12:39:13 -04:00
if imgRefAndAuth . Tag ( ) == "" && imgRefAndAuth . Digest ( ) != "" {
return fmt . Errorf ( "cannot use a digest reference for IMAGE:TAG" )
}
2017-08-25 17:49:40 -04:00
if imgRefAndAuth . Tag ( ) == "" && ! options . forceYes {
deleteRemote := command . PromptForConfirmation ( os . Stdin , cli . Out ( ) , fmt . Sprintf ( "Please confirm you would like to delete all signature data for %s?" , remote ) )
2017-08-24 18:46:01 -04:00
if ! deleteRemote {
fmt . Fprintf ( cli . Out ( ) , "\nAborting action.\n" )
return nil
}
}
2017-09-26 12:53:21 -04:00
notaryRepo , err := cli . NotaryClient ( imgRefAndAuth , trust . ActionsPushAndPull )
2017-08-24 18:46:01 -04:00
if err != nil {
return err
}
if err = clearChangeList ( notaryRepo ) ; err != nil {
return err
}
defer clearChangeList ( notaryRepo )
2017-08-25 17:49:40 -04:00
if err := revokeSignature ( notaryRepo , tag ) ; err != nil {
return errors . Wrapf ( err , "could not remove signature for %s" , remote )
2017-08-24 18:46:01 -04:00
}
fmt . Fprintf ( cli . Out ( ) , "Successfully deleted signature for %s\n" , remote )
return nil
}
2017-09-11 17:07:00 -04:00
func revokeSignature ( notaryRepo client . Repository , tag string ) error {
2017-08-24 18:46:01 -04:00
if tag != "" {
// Revoke signature for the specified tag
if err := revokeSingleSig ( notaryRepo , tag ) ; err != nil {
return err
}
} else {
// revoke all signatures for the image, as no tag was given
if err := revokeAllSigs ( notaryRepo ) ; err != nil {
return err
}
}
// Publish change
return notaryRepo . Publish ( )
}
2017-09-11 17:07:00 -04:00
func revokeSingleSig ( notaryRepo client . Repository , tag string ) error {
2017-08-24 18:46:01 -04:00
releasedTargetWithRole , err := notaryRepo . GetTargetByName ( tag , trust . ReleasesRole , data . CanonicalTargetsRole )
if err != nil {
return err
}
releasedTarget := releasedTargetWithRole . Target
return getSignableRolesForTargetAndRemove ( releasedTarget , notaryRepo )
}
2017-09-11 17:07:00 -04:00
func revokeAllSigs ( notaryRepo client . Repository ) error {
2017-08-24 18:46:01 -04:00
releasedTargetWithRoleList , err := notaryRepo . ListTargets ( trust . ReleasesRole , data . CanonicalTargetsRole )
if err != nil {
return err
}
2017-09-18 15:50:31 -04:00
if len ( releasedTargetWithRoleList ) == 0 {
return fmt . Errorf ( "no signed tags to remove" )
}
2017-08-24 18:46:01 -04:00
// we need all the roles that signed each released target so we can remove from all roles.
for _ , releasedTargetWithRole := range releasedTargetWithRoleList {
// remove from all roles
if err := getSignableRolesForTargetAndRemove ( releasedTargetWithRole . Target , notaryRepo ) ; err != nil {
return err
}
}
return nil
}
// get all the roles that signed the target and removes it from all roles.
2017-09-11 17:07:00 -04:00
func getSignableRolesForTargetAndRemove ( releasedTarget client . Target , notaryRepo client . Repository ) error {
2017-08-25 17:49:40 -04:00
signableRoles , err := trust . GetSignableRoles ( notaryRepo , & releasedTarget )
2017-08-24 18:46:01 -04:00
if err != nil {
return err
}
// remove from all roles
return notaryRepo . RemoveTarget ( releasedTarget . Name , signableRoles ... )
}