-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Add ipfs cli 'rotate' command to rotate identity private keys #7515
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
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."), | ||
}, | ||
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 | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
#!/usr/bin/env bash | ||
|
||
test_description="Test rotate command" | ||
|
||
. lib/test-lib.sh | ||
|
||
test_rotate() { | ||
FROM_ALG=$1 | ||
TO_ALG=$2 | ||
|
||
test_expect_success "ipfs init (from $FROM_ALG, to $TO_ALG)" ' | ||
export IPFS_PATH="$(pwd)/.ipfs" && | ||
case $FROM_ALG in | ||
rsa) | ||
ipfs init --profile=test -a=rsa > /dev/null | ||
;; | ||
ed25519) | ||
ipfs init --profile=test -a=ed25519 > /dev/null | ||
;; | ||
*) | ||
ipfs init --profile=test > /dev/null | ||
;; | ||
esac | ||
' | ||
|
||
test_expect_success "Save first ID and key" ' | ||
ipfs id -f="<id>" > first_id && | ||
ipfs id -f="<pubkey>" > first_key | ||
' | ||
|
||
test_launch_ipfs_daemon | ||
|
||
test_kill_ipfs_daemon | ||
|
||
test_expect_success "rotating keys" ' | ||
case $TO_ALG in | ||
rsa) | ||
ipfs rotate -a=rsa -b=2048 --oldkey=oldkey | ||
;; | ||
ed25519) | ||
ipfs rotate -a=ed25519 --oldkey=oldkey | ||
;; | ||
*) | ||
ipfs rotate --oldkey=oldkey | ||
;; | ||
esac | ||
' | ||
|
||
test_expect_success "Compare second ID and key to first" ' | ||
ipfs id -f="<id>" > second_id && | ||
ipfs id -f="<pubkey>" > second_key && | ||
! test_cmp first_id second_id && | ||
! test_cmp first_key second_key | ||
' | ||
|
||
test_expect_success "checking ID" ' | ||
ipfs config Identity.PeerID > expected-id && | ||
ipfs id -f "<id>\n" > actual-id && | ||
ipfs key list -l | grep self | cut -d " " -f1 > keystore-id && | ||
ipfs key list -l | grep oldkey | cut -d " " -f1 | tr -d "\n" > old-keystore-id && | ||
test_cmp expected-id actual-id && | ||
test_cmp expected-id keystore-id && | ||
test_cmp old-keystore-id first_id | ||
' | ||
|
||
test_launch_ipfs_daemon | ||
|
||
test_expect_success "publish name with new and old keys" ' | ||
echo "hello world" > msg && | ||
ipfs add msg | cut -d " " -f2 | tr -d "\n" > msg_hash && | ||
ipfs name publish --offline --allow-offline --key=self $(cat msg_hash) && | ||
ipfs name publish --offline --allow-offline --key=oldkey $(cat msg_hash) | ||
' | ||
|
||
test_kill_ipfs_daemon | ||
|
||
test_expect_success "clean up ipfs dir" ' | ||
rm -rf "$IPFS_PATH" | ||
' | ||
|
||
} | ||
test_rotate 'rsa' '' | ||
test_rotate 'ed25519' '' | ||
test_rotate '' '' | ||
test_rotate 'rsa' 'rsa' | ||
test_rotate 'ed25519' 'rsa' | ||
test_rotate '' 'rsa' | ||
test_rotate 'rsa' 'ed25519' | ||
test_rotate 'ed25519' 'ed25519' | ||
test_rotate '' 'ed25519' | ||
|
||
test_done |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fine, although I think we can simplify it so that we:
test_expect_success
for these steps to make them easier to debug/notice.