2017-09-26 20:16:18 -04:00
package trust
import (
2017-10-10 13:16:01 -04:00
"bytes"
2017-10-25 13:45:10 -04:00
"encoding/pem"
2017-09-26 20:16:18 -04:00
"fmt"
2022-02-25 08:33:57 -05:00
"io"
2017-09-26 20:16:18 -04:00
"os"
2019-06-24 18:12:01 -04:00
"runtime"
2017-09-26 20:16:18 -04:00
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/trust"
2017-10-25 13:13:24 -04:00
"github.com/pkg/errors"
2017-09-26 20:16:18 -04:00
"github.com/spf13/cobra"
2017-10-30 12:21:41 -04:00
"github.com/theupdateframework/notary"
"github.com/theupdateframework/notary/storage"
"github.com/theupdateframework/notary/trustmanager"
tufutils "github.com/theupdateframework/notary/tuf/utils"
2017-09-26 20:16:18 -04:00
)
const (
2022-09-30 13:13:22 -04:00
nonOwnerReadWriteMask = 0 o077
2017-09-26 20:16:18 -04:00
)
type keyLoadOptions struct {
keyName string
}
func newKeyLoadCommand ( dockerCli command . Streams ) * cobra . Command {
var options keyLoadOptions
cmd := & cobra . Command {
2017-10-25 13:45:10 -04:00
Use : "load [OPTIONS] KEYFILE" ,
2017-09-26 20:16:18 -04:00
Short : "Load a private key file for signing" ,
Args : cli . ExactArgs ( 1 ) ,
RunE : func ( cmd * cobra . Command , args [ ] string ) error {
return loadPrivKey ( dockerCli , args [ 0 ] , options )
} ,
}
flags := cmd . Flags ( )
2017-10-25 13:45:10 -04:00
flags . StringVar ( & options . keyName , "name" , "signer" , "Name for the loaded key" )
2017-09-26 20:16:18 -04:00
return cmd
}
func loadPrivKey ( streams command . Streams , keyPath string , options keyLoadOptions ) error {
2017-10-25 13:45:10 -04:00
// validate the key name if provided
if options . keyName != "" && ! validKeyName ( options . keyName ) {
return fmt . Errorf ( "key name \"%s\" must start with lowercase alphanumeric characters and can include \"-\" or \"_\" after the first character" , options . keyName )
}
2017-09-26 20:16:18 -04:00
trustDir := trust . GetTrustDirectory ( )
2017-10-10 13:16:01 -04:00
keyFileStore , err := storage . NewPrivateKeyFileStorage ( trustDir , notary . KeyExtension )
2017-09-26 20:16:18 -04:00
if err != nil {
return err
}
2017-10-23 05:23:39 -04:00
privKeyImporters := [ ] trustmanager . Importer { keyFileStore }
2017-09-26 20:16:18 -04:00
2017-10-25 13:45:10 -04:00
fmt . Fprintf ( streams . Out ( ) , "Loading key from \"%s\"...\n" , keyPath )
2017-09-26 20:16:18 -04:00
// Always use a fresh passphrase retriever for each import
passRet := trust . GetPassphraseRetriever ( streams . In ( ) , streams . Out ( ) )
2017-10-10 13:16:01 -04:00
keyBytes , err := getPrivKeyBytesFromPath ( keyPath )
if err != nil {
2017-10-25 13:45:10 -04:00
return errors . Wrapf ( err , "refusing to load key from %s" , keyPath )
2017-10-10 13:16:01 -04:00
}
if err := loadPrivKeyBytesToStore ( keyBytes , privKeyImporters , keyPath , options . keyName , passRet ) ; err != nil {
2017-10-25 13:13:24 -04:00
return errors . Wrapf ( err , "error importing key from %s" , keyPath )
2017-09-26 20:16:18 -04:00
}
fmt . Fprintf ( streams . Out ( ) , "Successfully imported key from %s\n" , keyPath )
return nil
}
2017-10-10 13:16:01 -04:00
func getPrivKeyBytesFromPath ( keyPath string ) ( [ ] byte , error ) {
2019-06-24 18:12:01 -04:00
if runtime . GOOS != "windows" {
fileInfo , err := os . Stat ( keyPath )
if err != nil {
return nil , err
}
if fileInfo . Mode ( ) & nonOwnerReadWriteMask != 0 {
return nil , fmt . Errorf ( "private key file %s must not be readable or writable by others" , keyPath )
}
2017-09-26 20:16:18 -04:00
}
from , err := os . OpenFile ( keyPath , os . O_RDONLY , notary . PrivExecPerms )
if err != nil {
2017-10-10 13:16:01 -04:00
return nil , err
2017-09-26 20:16:18 -04:00
}
defer from . Close ( )
2022-02-25 08:33:57 -05:00
return io . ReadAll ( from )
2017-10-10 13:16:01 -04:00
}
2017-10-23 05:23:39 -04:00
func loadPrivKeyBytesToStore ( privKeyBytes [ ] byte , privKeyImporters [ ] trustmanager . Importer , keyPath , keyName string , passRet notary . PassRetriever ) error {
2017-10-25 13:45:10 -04:00
var err error
if _ , _ , err = tufutils . ExtractPrivateKeyAttributes ( privKeyBytes ) ; err != nil {
2017-10-09 13:44:52 -04:00
return fmt . Errorf ( "provided file %s is not a supported private key - to add a signer's public key use docker trust signer add" , keyPath )
2017-09-26 20:16:18 -04:00
}
2017-10-25 13:45:10 -04:00
if privKeyBytes , err = decodePrivKeyIfNecessary ( privKeyBytes , passRet ) ; err != nil {
return errors . Wrapf ( err , "cannot load key from provided file %s" , keyPath )
}
2017-10-10 13:16:01 -04:00
// Make a reader, rewind the file pointer
2017-10-23 05:23:39 -04:00
return trustmanager . ImportKeys ( bytes . NewReader ( privKeyBytes ) , privKeyImporters , keyName , "" , passRet )
2017-09-26 20:16:18 -04:00
}
2017-10-25 13:45:10 -04:00
func decodePrivKeyIfNecessary ( privPemBytes [ ] byte , passRet notary . PassRetriever ) ( [ ] byte , error ) {
pemBlock , _ := pem . Decode ( privPemBytes )
_ , containsDEKInfo := pemBlock . Headers [ "DEK-Info" ]
if containsDEKInfo || pemBlock . Type == "ENCRYPTED PRIVATE KEY" {
// if we do not have enough information to properly import, try to decrypt the key
if _ , ok := pemBlock . Headers [ "path" ] ; ! ok {
privKey , _ , err := trustmanager . GetPasswdDecryptBytes ( passRet , privPemBytes , "" , "encrypted" )
if err != nil {
linting: fmt.Errorf can be replaced with errors.New (perfsprint)
internal/test/cli.go:175:14: fmt.Errorf can be replaced with errors.New (perfsprint)
return nil, fmt.Errorf("no notary client available unless defined")
^
cli/command/cli.go:318:29: fmt.Errorf can be replaced with errors.New (perfsprint)
return docker.Endpoint{}, fmt.Errorf("no context store initialized")
^
cli/command/container/attach.go:161:11: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf(result.Error.Message)
^
cli/command/container/opts.go:577:16: fmt.Errorf can be replaced with errors.New (perfsprint)
return nil, fmt.Errorf("--health-start-period cannot be negative")
^
cli/command/container/opts.go:580:16: fmt.Errorf can be replaced with errors.New (perfsprint)
return nil, fmt.Errorf("--health-start-interval cannot be negative")
^
cli/command/container/stats.go:221:11: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("filtering is not supported when specifying a list of containers")
^
cli/command/container/attach_test.go:82:17: fmt.Errorf can be replaced with errors.New (perfsprint)
expectedErr = fmt.Errorf("unexpected error")
^
cli/command/container/create_test.go:234:40: fmt.Errorf can be replaced with errors.New (perfsprint)
return container.CreateResponse{}, fmt.Errorf("shouldn't try to pull image")
^
cli/command/container/list_test.go:150:17: fmt.Errorf can be replaced with errors.New (perfsprint)
return nil, fmt.Errorf("error listing containers")
^
cli/command/container/rm_test.go:40:31: fmt.Errorf can be replaced with errors.New (perfsprint)
return errdefs.NotFound(fmt.Errorf("Error: no such container: " + container))
^
cli/command/container/run_test.go:138:40: fmt.Errorf can be replaced with errors.New (perfsprint)
return container.CreateResponse{}, fmt.Errorf("shouldn't try to pull image")
^
cli/command/image/pull_test.go:115:49: fmt.Errorf can be replaced with errors.New (perfsprint)
return io.NopCloser(strings.NewReader("")), fmt.Errorf("shouldn't try to pull image")
^
cli/command/network/connect.go:88:16: fmt.Errorf can be replaced with errors.New (perfsprint)
return nil, fmt.Errorf("invalid key/value pair format in driver options")
^
cli/command/plugin/create_test.go:96:11: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("Error creating plugin")
^
cli/command/plugin/disable_test.go:32:12: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("Error disabling plugin")
^
cli/command/plugin/enable_test.go:32:12: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("failed to enable plugin")
^
cli/command/plugin/inspect_test.go:55:22: fmt.Errorf can be replaced with errors.New (perfsprint)
return nil, nil, fmt.Errorf("error inspecting plugin")
^
cli/command/plugin/install_test.go:43:17: fmt.Errorf can be replaced with errors.New (perfsprint)
return nil, fmt.Errorf("Error installing plugin")
^
cli/command/plugin/install_test.go:51:17: fmt.Errorf can be replaced with errors.New (perfsprint)
return nil, fmt.Errorf("(image) when fetching")
^
cli/command/plugin/install_test.go:95:17: fmt.Errorf can be replaced with errors.New (perfsprint)
return nil, fmt.Errorf("should not try to install plugin")
^
cli/command/plugin/list_test.go:35:41: fmt.Errorf can be replaced with errors.New (perfsprint)
return types.PluginsListResponse{}, fmt.Errorf("error listing plugins")
^
cli/command/plugin/remove_test.go:27:12: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("Error removing plugin")
^
cli/command/registry/login_test.go:36:46: fmt.Errorf can be replaced with errors.New (perfsprint)
return registrytypes.AuthenticateOKBody{}, fmt.Errorf("Invalid Username or Password")
^
cli/command/registry/login_test.go:44:46: fmt.Errorf can be replaced with errors.New (perfsprint)
return registrytypes.AuthenticateOKBody{}, fmt.Errorf(errUnknownUser)
^
cli/command/system/info.go:190:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("errors pretty printing info")
^
cli/command/system/prune.go:77:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf(`ERROR: The "until" filter is not supported with "--volumes"`)
^
cli/command/system/version_test.go:19:28: fmt.Errorf can be replaced with errors.New (perfsprint)
return types.Version{}, fmt.Errorf("no server")
^
cli/command/trust/key_load.go:112:22: fmt.Errorf can be replaced with errors.New (perfsprint)
return []byte{}, fmt.Errorf("could not decrypt key")
^
cli/command/trust/revoke.go:44:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("cannot use a digest reference for IMAGE:TAG")
^
cli/command/trust/revoke.go:105:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("no signed tags to remove")
^
cli/command/trust/signer_add.go:56:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("releases is a reserved keyword, please use a different signer name")
^
cli/command/trust/signer_add.go:60:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("path to a public key must be provided using the `--key` flag")
^
opts/config.go:71:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("source is required")
^
opts/mount.go:168:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("type is required")
^
opts/mount.go:172:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("target is required")
^
opts/network.go:90:11: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("network name/id is not specified")
^
opts/network.go:129:18: fmt.Errorf can be replaced with errors.New (perfsprint)
return "", "", fmt.Errorf("invalid key value pair format in driver options")
^
opts/opts.go:404:13: fmt.Errorf can be replaced with errors.New (perfsprint)
return 0, fmt.Errorf("value is too precise")
^
opts/opts.go:412:18: fmt.Errorf can be replaced with errors.New (perfsprint)
return "", "", fmt.Errorf("empty string specified for links")
^
opts/parse.go:84:37: fmt.Errorf can be replaced with errors.New (perfsprint)
return container.RestartPolicy{}, fmt.Errorf("invalid restart policy format: no policy provided before colon")
^
opts/parse.go:89:38: fmt.Errorf can be replaced with errors.New (perfsprint)
return container.RestartPolicy{}, fmt.Errorf("invalid restart policy format: maximum retry count must be an integer")
^
opts/port.go:105:13: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("hostip is not supported")
^
opts/secret.go:70:10: fmt.Errorf can be replaced with errors.New (perfsprint)
return fmt.Errorf("source is required")
^
opts/env_test.go:57:11: fmt.Errorf can be replaced with errors.New (perfsprint)
err: fmt.Errorf("invalid environment variable: =a"),
^
opts/env_test.go:93:11: fmt.Errorf can be replaced with errors.New (perfsprint)
err: fmt.Errorf("invalid environment variable: ="),
^
cli-plugins/manager/error_test.go:16:11: fmt.Errorf can be replaced with errors.New (perfsprint)
inner := fmt.Errorf("testing")
^
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-06-10 14:47:07 -04:00
return [ ] byte { } , errors . New ( "could not decrypt key" )
2017-10-25 13:45:10 -04:00
}
privPemBytes = privKey . Private ( )
}
}
return privPemBytes , nil
}