@cryptotaxi247 / kubo / commits / 2c255eab2

feat: implement ipfs rotate command for rotating the ID keys for the node

Petar Maymounkov committed Jun 30, 2020 at 16:20 UTC 2c255eab2420818b0d1050f0797ae3265758ce50
4 files changed +209 -1
cmd/ipfs/ipfs.go
+1
@@ -24,6 +24,7 @@ var commandsClientCmd = commands.CommandsCmd(Root)
24 var localCommands = map[string]*cmds.Command{
25 "daemon": daemonCmd,
26 "init": initCmd,
27 + "rotate": rotateCmd,
28 "commands": commandsClientCmd,
29 }
30
cmd/ipfs/rotate.go new
+115
@@ -0,0 +1,115 @@
1 +package main
2 +
3 +import (
4 + "fmt"
5 + "io"
6 + "os"
7 +
8 + cmds "github.com/ipfs/go-ipfs-cmds"
9 + config "github.com/ipfs/go-ipfs-config"
10 + oldcmds "github.com/ipfs/go-ipfs/commands"
11 + fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
12 + "github.com/ipfs/interface-go-ipfs-core/options"
13 +)
14 +
15 +const (
16 + oldKeyOptionName = "oldkey"
17 +)
18 +
19 +var rotateCmd = &cmds.Command{
20 + Helptext: cmds.HelpText{
21 + Tagline: "Rotates the ipfs identity.",
22 + ShortDescription: `
23 +Generates a new ipfs identity and saves it to the ipfs config file.
24 +The daemon must not be running when calling this command.
25 +
26 +ipfs uses a repository in the local file system. By default, the repo is
27 +located at ~/.ipfs. To change the repo location, set the $IPFS_PATH
28 +environment variable:
29 +
30 + export IPFS_PATH=/path/to/ipfsrepo
31 +`,
32 + },
33 + Arguments: []cmds.Argument{},
34 + Options: []cmds.Option{
35 + cmds.StringOption(oldKeyOptionName, "o", "Keystore name for the old/rotated-out key."),
36 + cmds.StringOption(algorithmOptionName, "a", "Cryptographic algorithm to use for key generation.").WithDefault(algorithmDefault),
37 + cmds.IntOption(bitsOptionName, "b", "Number of bits to use in the generated RSA private key."),
38 + },
39 + PreRun: func(req *cmds.Request, env cmds.Environment) error {
40 + cctx := env.(*oldcmds.Context)
41 + daemonLocked, err := fsrepo.LockedByOtherProcess(cctx.ConfigRoot)
42 + if err != nil {
43 + return err
44 + }
45 +
46 + log.Info("checking if daemon is running...")
47 + if daemonLocked {
48 + log.Debug("ipfs daemon is running")
49 + e := "ipfs daemon is running. please stop it to run this command"
50 + return cmds.ClientError(e)
51 + }
52 +
53 + return nil
54 + },
55 + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
56 + cctx := env.(*oldcmds.Context)
57 + nBitsForKeypair, nBitsGiven := req.Options[bitsOptionName].(int)
58 + algorithm, _ := req.Options[algorithmOptionName].(string)
59 + oldKey, ok := req.Options[oldKeyOptionName].(string)
60 + if !ok {
61 + return fmt.Errorf("keystore name for backing up old key must be provided")
62 + }
63 + return doRotate(os.Stdout, cctx.ConfigRoot, oldKey, algorithm, nBitsForKeypair, nBitsGiven)
64 + },
65 +}
66 +
67 +func doRotate(out io.Writer, repoRoot string, oldKey string, algorithm string, nBitsForKeypair int, nBitsGiven bool) error {
68 + // Open repo
69 + repo, err := fsrepo.Open(repoRoot)
70 + if err != nil {
71 + return fmt.Errorf("opening repo (%v)", err)
72 + }
73 + defer repo.Close()
74 +
75 + // Read config file from repo
76 + cfg, err := repo.Config()
77 + if err != nil {
78 + return fmt.Errorf("reading config from repo (%v)", err)
79 + }
80 +
81 + // Generate new identity
82 + var identity config.Identity
83 + if nBitsGiven {
84 + identity, err = config.CreateIdentity(out, []options.KeyGenerateOption{
85 + options.Key.Size(nBitsForKeypair),
86 + options.Key.Type(algorithm),
87 + })
88 + } else {
89 + identity, err = config.CreateIdentity(out, []options.KeyGenerateOption{
90 + options.Key.Type(algorithm),
91 + })
92 + }
93 + if err != nil {
94 + return fmt.Errorf("creating identity (%v)", err)
95 + }
96 +
97 + // Save old identity to keystore
98 + oldPrivKey, err := cfg.Identity.DecodePrivateKey("")
99 + if err != nil {
100 + return fmt.Errorf("decoding old private key (%v)", err)
101 + }
102 + keystore := repo.Keystore()
103 + if err := keystore.Put(oldKey, oldPrivKey); err != nil {
104 + return fmt.Errorf("saving old key in keystore (%v)", err)
105 + }
106 +
107 + // Update identity
108 + cfg.Identity = identity
109 +
110 + // Write config file to repo
111 + if err = repo.SetConfig(cfg); err != nil {
112 + return fmt.Errorf("saving new key to config (%v)", err)
113 + }
114 + return nil
115 +}
test/sharness/lib/test-lib.sh
+1 -1
@@ -193,7 +193,7 @@ test_init_ipfs() {
193
194 test_expect_success "ipfs init succeeds" '
195 export IPFS_PATH="$(pwd)/.ipfs" &&
196 - ipfs init --profile=test -b=2048 > /dev/null
196 + ipfs init --profile=test -a=rsa -b=2048 > /dev/null
197 '
198
199 test_expect_success "prepare config -- mounting" '
test/sharness/t0027-rotate.sh new
+92
@@ -0,0 +1,92 @@
1 +#!/usr/bin/env bash
2 +
3 +test_description="Test rotate command"
4 +
5 +. lib/test-lib.sh
6 +
7 +test_rotate() {
8 + FROM_ALG=$1
9 + TO_ALG=$2
10 +
11 + test_expect_success "ipfs init (from $FROM_ALG, to $TO_ALG)" '
12 + export IPFS_PATH="$(pwd)/.ipfs" &&
13 + case $FROM_ALG in
14 + rsa)
15 + ipfs init --profile=test -a=rsa > /dev/null
16 + ;;
17 + ed25519)
18 + ipfs init --profile=test -a=ed25519 > /dev/null
19 + ;;
20 + *)
21 + ipfs init --profile=test > /dev/null
22 + ;;
23 + esac
24 + '
25 +
26 + test_expect_success "Save first ID and key" '
27 + ipfs id -f="<id>" > first_id &&
28 + ipfs id -f="<pubkey>" > first_key
29 + '
30 +
31 + test_launch_ipfs_daemon
32 +
33 + test_kill_ipfs_daemon
34 +
35 + test_expect_success "rotating keys" '
36 + case $TO_ALG in
37 + rsa)
38 + ipfs rotate -a=rsa -b=2048 --oldkey=oldkey
39 + ;;
40 + ed25519)
41 + ipfs rotate -a=ed25519 --oldkey=oldkey
42 + ;;
43 + *)
44 + ipfs rotate --oldkey=oldkey
45 + ;;
46 + esac
47 + '
48 +
49 + test_expect_success "Compare second ID and key to first" '
50 + ipfs id -f="<id>" > second_id &&
51 + ipfs id -f="<pubkey>" > second_key &&
52 + ! test_cmp first_id second_id &&
53 + ! test_cmp first_key second_key
54 + '
55 +
56 + test_expect_success "checking ID" '
57 + ipfs config Identity.PeerID > expected-id &&
58 + ipfs id -f "<id>\n" > actual-id &&
59 + ipfs key list -l | grep self | cut -d " " -f1 > keystore-id &&
60 + ipfs key list -l | grep oldkey | cut -d " " -f1 | tr -d "\n" > old-keystore-id &&
61 + test_cmp expected-id actual-id &&
62 + test_cmp expected-id keystore-id &&
63 + test_cmp old-keystore-id first_id
64 + '
65 +
66 + test_launch_ipfs_daemon
67 +
68 + test_expect_success "publish name with new and old keys" '
69 + echo "hello world" > msg &&
70 + ipfs add msg | cut -d " " -f2 | tr -d "\n" > msg_hash &&
71 + ipfs name publish --offline --allow-offline --key=self $(cat msg_hash) &&
72 + ipfs name publish --offline --allow-offline --key=oldkey $(cat msg_hash)
73 + '
74 +
75 + test_kill_ipfs_daemon
76 +
77 + test_expect_success "clean up ipfs dir" '
78 + rm -rf "$IPFS_PATH"
79 + '
80 +
81 +}
82 +test_rotate 'rsa' ''
83 +test_rotate 'ed25519' ''
84 +test_rotate '' ''
85 +test_rotate 'rsa' 'rsa'
86 +test_rotate 'ed25519' 'rsa'
87 +test_rotate '' 'rsa'
88 +test_rotate 'rsa' 'ed25519'
89 +test_rotate 'ed25519' 'ed25519'
90 +test_rotate '' 'ed25519'
91 +
92 +test_done