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"
"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"
2024-03-12 07:38:47 -04:00
"github.com/docker/docker/errdefs"
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
}
2023-11-20 11:38:50 -05:00
func newRevokeCommand ( dockerCLI command . Cli ) * cobra . Command {
2017-08-24 18:46:01 -04:00
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 {
2023-09-09 18:27:44 -04:00
return revokeTrust ( cmd . Context ( ) , dockerCLI , args [ 0 ] , options )
2017-08-24 18:46:01 -04:00
} ,
}
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
}
2023-09-09 18:27:44 -04:00
func revokeTrust ( ctx context . Context , dockerCLI command . Cli , remote string , options revokeOptions ) error {
2023-11-20 11:38:50 -05:00
imgRefAndAuth , err := trust . GetImageReferencesAndAuth ( ctx , image . AuthResolver ( dockerCLI ) , 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 {
2024-02-21 10:36:17 -05:00
deleteRemote , err := command . PromptForConfirmation ( ctx , dockerCLI . In ( ) , dockerCLI . Out ( ) , fmt . Sprintf ( "Please confirm you would like to delete all signature data for %s?" , remote ) )
if err != nil {
2024-03-12 07:38:47 -04:00
return err
2024-02-21 10:36:17 -05:00
}
2017-08-24 18:46:01 -04:00
if ! deleteRemote {
2024-04-02 09:44:23 -04:00
return errdefs . Cancelled ( errors . New ( "trust revoke has been cancelled" ) )
2017-08-24 18:46:01 -04:00
}
}
2023-11-20 11:38:50 -05:00
notaryRepo , err := dockerCLI . 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
}
2023-11-20 11:38:50 -05:00
fmt . Fprintf ( dockerCLI . Out ( ) , "Successfully deleted signature for %s\n" , remote )
2017-08-24 18:46:01 -04:00
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 ... )
}