Merge pull request #7599 from ipfs/feat/move-key-rotate

Move key rotation command to ipfs key rotate
This commit is contained in:
Adin Schmahmann 2020-08-17 13:14:03 -04:00 committed by GitHub
commit 992e4a71ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 113 additions and 125 deletions

View File

@ -22,7 +22,6 @@ var commandsClientCmd = commands.CommandsCmd(Root)
var localCommands = map[string]*cmds.Command{
"daemon": daemonCmd,
"init": initCmd,
"rotate": rotateCmd,
"commands": commandsClientCmd,
}

View File

@ -1,116 +0,0 @@
package main
import (
"fmt"
"io"
"os"
cmds "github.com/ipfs/go-ipfs-cmds"
config "github.com/ipfs/go-ipfs-config"
oldcmds "github.com/ipfs/go-ipfs/commands"
fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
"github.com/ipfs/interface-go-ipfs-core/options"
)
const (
oldKeyOptionName = "oldkey"
)
var rotateCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Rotates the ipfs identity.",
ShortDescription: `
Generates a new ipfs identity and saves it to the ipfs config file.
The daemon must not be running when calling this command.
ipfs uses a repository in the local file system. By default, the repo is
located at ~/.ipfs. To change the repo location, set the $IPFS_PATH
environment variable:
export IPFS_PATH=/path/to/ipfsrepo
`,
},
Arguments: []cmds.Argument{},
Options: []cmds.Option{
cmds.StringOption(oldKeyOptionName, "o", "Keystore name for the old/rotated-out key."),
cmds.StringOption(algorithmOptionName, "a", "Cryptographic algorithm to use for key generation.").WithDefault(algorithmDefault),
cmds.IntOption(bitsOptionName, "b", "Number of bits to use in the generated RSA private key."),
},
NoRemote: true,
PreRun: func(req *cmds.Request, env cmds.Environment) error {
cctx := env.(*oldcmds.Context)
daemonLocked, err := fsrepo.LockedByOtherProcess(cctx.ConfigRoot)
if err != nil {
return err
}
log.Info("checking if daemon is running...")
if daemonLocked {
log.Debug("ipfs daemon is running")
e := "ipfs daemon is running. please stop it to run this command"
return cmds.ClientError(e)
}
return nil
},
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
cctx := env.(*oldcmds.Context)
nBitsForKeypair, nBitsGiven := req.Options[bitsOptionName].(int)
algorithm, _ := req.Options[algorithmOptionName].(string)
oldKey, ok := req.Options[oldKeyOptionName].(string)
if !ok {
return fmt.Errorf("keystore name for backing up old key must be provided")
}
return doRotate(os.Stdout, cctx.ConfigRoot, oldKey, algorithm, nBitsForKeypair, nBitsGiven)
},
}
func doRotate(out io.Writer, repoRoot string, oldKey string, algorithm string, nBitsForKeypair int, nBitsGiven bool) error {
// Open repo
repo, err := fsrepo.Open(repoRoot)
if err != nil {
return fmt.Errorf("opening repo (%v)", err)
}
defer repo.Close()
// Read config file from repo
cfg, err := repo.Config()
if err != nil {
return fmt.Errorf("reading config from repo (%v)", err)
}
// Generate new identity
var identity config.Identity
if nBitsGiven {
identity, err = config.CreateIdentity(out, []options.KeyGenerateOption{
options.Key.Size(nBitsForKeypair),
options.Key.Type(algorithm),
})
} else {
identity, err = config.CreateIdentity(out, []options.KeyGenerateOption{
options.Key.Type(algorithm),
})
}
if err != nil {
return fmt.Errorf("creating identity (%v)", err)
}
// Save old identity to keystore
oldPrivKey, err := cfg.Identity.DecodePrivateKey("")
if err != nil {
return fmt.Errorf("decoding old private key (%v)", err)
}
keystore := repo.Keystore()
if err := keystore.Put(oldKey, oldPrivKey); err != nil {
return fmt.Errorf("saving old key in keystore (%v)", err)
}
// Update identity
cfg.Identity = identity
// Write config file to repo
if err = repo.SetConfig(cfg); err != nil {
return fmt.Errorf("saving new key to config (%v)", err)
}
return nil
}

View File

@ -138,6 +138,7 @@ func TestCommands(t *testing.T) {
"/key/list",
"/key/rename",
"/key/rm",
"/key/rotate",
"/log",
"/log/level",
"/log/ls",

View File

@ -11,6 +11,8 @@ import (
"text/tabwriter"
cmds "github.com/ipfs/go-ipfs-cmds"
config "github.com/ipfs/go-ipfs-config"
oldcmds "github.com/ipfs/go-ipfs/commands"
cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv"
"github.com/ipfs/go-ipfs/core/commands/e"
fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
@ -44,6 +46,7 @@ publish'.
"list": keyListCmd,
"rename": keyRenameCmd,
"rm": keyRmCmd,
"rotate": keyRotateCmd,
},
}
@ -65,9 +68,11 @@ type KeyRenameOutput struct {
}
const (
keyStoreTypeOptionName = "type"
keyStoreSizeOptionName = "size"
keyFormatOptionName = "format"
keyStoreAlgorithmDefault = options.RSAKey
keyStoreTypeOptionName = "type"
keyStoreSizeOptionName = "size"
keyFormatOptionName = "format"
oldKeyOptionName = "oldkey"
)
var keyGenCmd = &cmds.Command{
@ -75,7 +80,7 @@ var keyGenCmd = &cmds.Command{
Tagline: "Create a new keypair",
},
Options: []cmds.Option{
cmds.StringOption(keyStoreTypeOptionName, "t", "type of the key to create: rsa, ed25519").WithDefault("rsa"),
cmds.StringOption(keyStoreTypeOptionName, "t", "type of the key to create: rsa, ed25519").WithDefault(keyStoreAlgorithmDefault),
cmds.IntOption(keyStoreSizeOptionName, "s", "size of the key to generate"),
cmds.StringOption(keyFormatOptionName, "f", "output format: b58mh or b36cid").WithDefault("b58mh"),
},
@ -413,6 +418,105 @@ var keyRmCmd = &cmds.Command{
Type: KeyOutputList{},
}
var keyRotateCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Rotates the ipfs identity.",
ShortDescription: `
Generates a new ipfs identity and saves it to the ipfs config file.
The daemon must not be running when calling this command.
ipfs uses a repository in the local file system. By default, the repo is
located at ~/.ipfs. To change the repo location, set the $IPFS_PATH
environment variable:
export IPFS_PATH=/path/to/ipfsrepo
`,
},
Arguments: []cmds.Argument{},
Options: []cmds.Option{
cmds.StringOption(oldKeyOptionName, "o", "Keystore name for the old/rotated-out key."),
cmds.StringOption(keyStoreTypeOptionName, "t", "type of the key to create: rsa, ed25519").WithDefault(keyStoreAlgorithmDefault),
cmds.IntOption(keyStoreSizeOptionName, "s", "size of the key to generate"),
},
NoRemote: true,
PreRun: func(req *cmds.Request, env cmds.Environment) error {
cctx := env.(*oldcmds.Context)
daemonLocked, err := fsrepo.LockedByOtherProcess(cctx.ConfigRoot)
if err != nil {
return err
}
log.Info("checking if daemon is running...")
if daemonLocked {
log.Debug("ipfs daemon is running")
e := "ipfs daemon is running. please stop it to run this command"
return cmds.ClientError(e)
}
return nil
},
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
cctx := env.(*oldcmds.Context)
nBitsForKeypair, nBitsGiven := req.Options[keyStoreSizeOptionName].(int)
algorithm, _ := req.Options[keyStoreTypeOptionName].(string)
oldKey, ok := req.Options[oldKeyOptionName].(string)
if !ok {
return fmt.Errorf("keystore name for backing up old key must be provided")
}
return doRotate(os.Stdout, cctx.ConfigRoot, oldKey, algorithm, nBitsForKeypair, nBitsGiven)
},
}
func doRotate(out io.Writer, repoRoot string, oldKey string, algorithm string, nBitsForKeypair int, nBitsGiven bool) error {
// Open repo
repo, err := fsrepo.Open(repoRoot)
if err != nil {
return fmt.Errorf("opening repo (%v)", err)
}
defer repo.Close()
// Read config file from repo
cfg, err := repo.Config()
if err != nil {
return fmt.Errorf("reading config from repo (%v)", err)
}
// Generate new identity
var identity config.Identity
if nBitsGiven {
identity, err = config.CreateIdentity(out, []options.KeyGenerateOption{
options.Key.Size(nBitsForKeypair),
options.Key.Type(algorithm),
})
} else {
identity, err = config.CreateIdentity(out, []options.KeyGenerateOption{
options.Key.Type(algorithm),
})
}
if err != nil {
return fmt.Errorf("creating identity (%v)", err)
}
// Save old identity to keystore
oldPrivKey, err := cfg.Identity.DecodePrivateKey("")
if err != nil {
return fmt.Errorf("decoding old private key (%v)", err)
}
keystore := repo.Keystore()
if err := keystore.Put(oldKey, oldPrivKey); err != nil {
return fmt.Errorf("saving old key in keystore (%v)", err)
}
// Update identity
cfg.Identity = identity
// Write config file to repo
if err = repo.SetConfig(cfg); err != nil {
return fmt.Errorf("saving new key to config (%v)", err)
}
return nil
}
func verifyIDFormatLabel(formatLabel string) error {
switch formatLabel {
case "b58mh":

View File

@ -146,7 +146,7 @@ func genKeys(t *testing.T, keyType int) (ci.PrivKey, peer.ID, string, string) {
return sk, id, PkKeyForID(id), ipns.RecordKey(id)
}
func createIPNSRecordWithEmbeddedPublicKey(sk ci.PrivKey, val []byte, seq uint64, eol time.Time) (*ipns_pb.IpnsEntry, error){
func createIPNSRecordWithEmbeddedPublicKey(sk ci.PrivKey, val []byte, seq uint64, eol time.Time) (*ipns_pb.IpnsEntry, error) {
entry, err := ipns.Create(sk, val, seq, eol)
if err != nil {
return nil, err

View File

@ -35,13 +35,13 @@ test_rotate() {
test_expect_success "rotating keys" '
case $TO_ALG in
rsa)
ipfs rotate -a=rsa -b=2048 --oldkey=oldkey
ipfs key rotate -t=rsa -s=2048 --oldkey=oldkey
;;
ed25519)
ipfs rotate -a=ed25519 --oldkey=oldkey
ipfs key rotate -t=ed25519 --oldkey=oldkey
;;
*)
ipfs rotate --oldkey=oldkey
ipfs key rotate --oldkey=oldkey
;;
esac
'