Merge pull request #3914 from filecoin-project/feat/lotus-shed-more
Add keyinfo verify and jwt token command to lotus-shed
This commit is contained in:
commit
0dfd4f3e63
@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@ -8,10 +9,12 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gbrlsnchs/jwt/v3"
|
"github.com/gbrlsnchs/jwt/v3"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-jsonrpc/auth"
|
||||||
"github.com/filecoin-project/lotus/api/apistruct"
|
"github.com/filecoin-project/lotus/api/apistruct"
|
||||||
"github.com/filecoin-project/lotus/chain/types"
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
"github.com/filecoin-project/lotus/node/modules"
|
"github.com/filecoin-project/lotus/node/modules"
|
||||||
@ -24,6 +27,102 @@ var jwtCmd = &cli.Command{
|
|||||||
having to run the lotus daemon.`,
|
having to run the lotus daemon.`,
|
||||||
Subcommands: []*cli.Command{
|
Subcommands: []*cli.Command{
|
||||||
jwtNewCmd,
|
jwtNewCmd,
|
||||||
|
jwtTokenCmd,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var jwtTokenCmd = &cli.Command{
|
||||||
|
Name: "token",
|
||||||
|
Usage: "create a token for a given jwt secret",
|
||||||
|
ArgsUsage: "<name>",
|
||||||
|
Description: `The jwt tokens have four different levels of permissions that provide some ability
|
||||||
|
to control access to what methods can be invoked by the holder of the token.
|
||||||
|
|
||||||
|
This command only works on jwt secrets that are base16 encoded files, such as those produced by the
|
||||||
|
sibling 'new' command.
|
||||||
|
`,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "output",
|
||||||
|
Value: "token",
|
||||||
|
Usage: "specify a name",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "read",
|
||||||
|
Value: false,
|
||||||
|
Usage: "add read permissions to the token",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "write",
|
||||||
|
Value: false,
|
||||||
|
Usage: "add write permissions to the token",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "sign",
|
||||||
|
Value: false,
|
||||||
|
Usage: "add sign permissions to the token",
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "admin",
|
||||||
|
Value: false,
|
||||||
|
Usage: "add admin permissions to the token",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: func(cctx *cli.Context) error {
|
||||||
|
if !cctx.Args().Present() {
|
||||||
|
return fmt.Errorf("please specify a name")
|
||||||
|
}
|
||||||
|
|
||||||
|
inputFile, err := os.Open(cctx.Args().First())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer inputFile.Close() //nolint:errcheck
|
||||||
|
input := bufio.NewReader(inputFile)
|
||||||
|
|
||||||
|
encoded, err := ioutil.ReadAll(input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
decoded, err := hex.DecodeString(strings.TrimSpace(string(encoded)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var keyInfo types.KeyInfo
|
||||||
|
if err := json.Unmarshal(decoded, &keyInfo); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
perms := []auth.Permission{}
|
||||||
|
|
||||||
|
if cctx.Bool("read") {
|
||||||
|
perms = append(perms, apistruct.PermRead)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cctx.Bool("write") {
|
||||||
|
perms = append(perms, apistruct.PermWrite)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cctx.Bool("sign") {
|
||||||
|
perms = append(perms, apistruct.PermSign)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cctx.Bool("admin") {
|
||||||
|
perms = append(perms, apistruct.PermAdmin)
|
||||||
|
}
|
||||||
|
|
||||||
|
p := modules.JwtPayload{
|
||||||
|
Allow: perms,
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := jwt.Sign(&p, jwt.NewHS256(keyInfo.PrivateKey))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ioutil.WriteFile(cctx.String("output"), token, 0600)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,16 +9,22 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
|
"github.com/multiformats/go-base32"
|
||||||
|
|
||||||
"github.com/libp2p/go-libp2p-core/crypto"
|
"github.com/libp2p/go-libp2p-core/crypto"
|
||||||
"github.com/libp2p/go-libp2p-core/peer"
|
"github.com/libp2p/go-libp2p-core/peer"
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/chain/types"
|
"github.com/filecoin-project/lotus/chain/types"
|
||||||
"github.com/filecoin-project/lotus/chain/wallet"
|
"github.com/filecoin-project/lotus/chain/wallet"
|
||||||
|
"github.com/filecoin-project/lotus/node/modules"
|
||||||
"github.com/filecoin-project/lotus/node/modules/lp2p"
|
"github.com/filecoin-project/lotus/node/modules/lp2p"
|
||||||
"github.com/filecoin-project/lotus/node/repo"
|
"github.com/filecoin-project/lotus/node/repo"
|
||||||
|
|
||||||
@ -43,6 +49,90 @@ var keyinfoCmd = &cli.Command{
|
|||||||
keyinfoNewCmd,
|
keyinfoNewCmd,
|
||||||
keyinfoInfoCmd,
|
keyinfoInfoCmd,
|
||||||
keyinfoImportCmd,
|
keyinfoImportCmd,
|
||||||
|
keyinfoVerifyCmd,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var keyinfoVerifyCmd = &cli.Command{
|
||||||
|
Name: "verify",
|
||||||
|
Usage: "verify the filename of a keystore object on disk with it's contents",
|
||||||
|
Description: `Keystore objects are base32 enocded strings, with wallets being dynamically named via
|
||||||
|
the wallet address. This command can ensure that the naming of these keystore objects are correct`,
|
||||||
|
Action: func(cctx *cli.Context) error {
|
||||||
|
filePath := cctx.Args().First()
|
||||||
|
fileName := path.Base(filePath)
|
||||||
|
|
||||||
|
inputFile, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer inputFile.Close() //nolint:errcheck
|
||||||
|
input := bufio.NewReader(inputFile)
|
||||||
|
|
||||||
|
keyContent, err := ioutil.ReadAll(input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var keyInfo types.KeyInfo
|
||||||
|
if err := json.Unmarshal(keyContent, &keyInfo); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch keyInfo.Type {
|
||||||
|
case lp2p.KTLibp2pHost:
|
||||||
|
name, err := base32.RawStdEncoding.DecodeString(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("decoding key: '%s': %w", fileName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(name) != keyInfo.Type {
|
||||||
|
return fmt.Errorf("%s of type %s is incorrect", fileName, keyInfo.Type)
|
||||||
|
}
|
||||||
|
case modules.KTJwtHmacSecret:
|
||||||
|
name, err := base32.RawStdEncoding.DecodeString(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("decoding key: '%s': %w", fileName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(name) != modules.JWTSecretName {
|
||||||
|
return fmt.Errorf("%s of type %s is incorrect", fileName, keyInfo.Type)
|
||||||
|
}
|
||||||
|
case wallet.KTSecp256k1, wallet.KTBLS:
|
||||||
|
keystore := wallet.NewMemKeyStore()
|
||||||
|
w, err := wallet.NewWallet(keystore)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := w.Import(&keyInfo); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
list, err := keystore.List()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(list) != 1 {
|
||||||
|
return fmt.Errorf("Unexpected number of keys, expected 1, found %d", len(list))
|
||||||
|
}
|
||||||
|
|
||||||
|
name, err := base32.RawStdEncoding.DecodeString(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return xerrors.Errorf("decoding key: '%s': %w", fileName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(name) != list[0] {
|
||||||
|
return fmt.Errorf("%s of type %s; file is named for %s, but key is actually %s", fileName, keyInfo.Type, string(name), list[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Unknown keytype %s", keyInfo.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user